1use core::{alloc::Layout, cmp::PartialOrd, num::NonZeroU64, ptr::NonNull};
2
3use derive_more::{
4 Add, AddAssign, Debug, Display, Div, From, Into, Mul, MulAssign, Sub, SubAssign,
5};
6
7#[derive(
8 Debug,
9 Display,
10 Clone,
11 Copy,
12 PartialEq,
13 Eq,
14 PartialOrd,
15 Hash,
16 From,
17 Into,
18 Add,
19 AddAssign,
20 Mul,
21 MulAssign,
22 Sub,
23 SubAssign,
24 Div,
25)]
26#[debug("{}", format_args!("{_0:#X}"))]
27#[display("{}", format_args!("{_0:#X}"))]
28pub struct DmaAddr(u64);
29
30impl DmaAddr {
31 pub fn as_u64(&self) -> u64 {
32 self.0
33 }
34
35 pub fn checked_add(&self, rhs: u64) -> Option<Self> {
36 self.0.checked_add(rhs).map(DmaAddr)
37 }
38}
39
40impl PartialEq<u64> for DmaAddr {
41 fn eq(&self, other: &u64) -> bool {
42 self.0 == *other
43 }
44}
45
46impl PartialOrd<u64> for DmaAddr {
47 fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
48 self.0.partial_cmp(other)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct DmaDomainId(NonZeroU64);
58
59impl DmaDomainId {
60 pub const fn new(id: NonZeroU64) -> Self {
61 Self(id)
62 }
63
64 pub const fn legacy_global() -> Self {
67 Self(NonZeroU64::MIN)
68 }
69
70 pub fn from_raw(id: u64) -> Self {
71 Self(NonZeroU64::new(id).unwrap_or(NonZeroU64::MIN))
72 }
73
74 pub const fn get(self) -> NonZeroU64 {
75 self.0
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct DmaConstraints {
82 pub addr_mask: u64,
83 pub align: usize,
84 pub boundary: Option<usize>,
85 pub max_segment_size: Option<usize>,
86}
87
88impl DmaConstraints {
89 pub const fn new(addr_mask: u64) -> Self {
90 Self {
91 addr_mask,
92 align: 1,
93 boundary: None,
94 max_segment_size: None,
95 }
96 }
97
98 pub fn with_align(mut self, align: usize) -> Self {
99 self.align = align.max(1);
100 self
101 }
102
103 pub fn with_boundary(mut self, boundary: usize) -> Self {
104 self.boundary = Some(boundary.max(1));
105 self
106 }
107
108 pub fn with_max_segment_size(mut self, max_segment_size: usize) -> Self {
109 self.max_segment_size = Some(max_segment_size);
110 self
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub enum DmaDirection {
117 ToDevice,
119 FromDevice,
121 Bidirectional,
123}
124
125#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
126pub enum DmaError {
127 #[error("DMA allocation failed")]
128 NoMemory,
129 #[error("Invalid layout")]
130 LayoutError(#[from] core::alloc::LayoutError),
131 #[error("DMA address {addr} does not match device mask {mask:#X}")]
132 DmaMaskNotMatch { addr: DmaAddr, mask: u64 },
133 #[error("DMA align mismatch: required={required:#X}, but address={address}")]
134 AlignMismatch { required: usize, address: DmaAddr },
135 #[error("DMA segment size {size:#X} exceeds max segment size {max:#X}")]
136 SegmentTooLarge { size: usize, max: usize },
137 #[error("DMA address range crosses boundary {boundary:#X}: addr={addr}, size={size:#X}")]
138 BoundaryCross {
139 addr: DmaAddr,
140 size: usize,
141 boundary: usize,
142 },
143 #[error("Null pointer provided for DMA mapping")]
144 NullPointer,
145 #[error("Zero-sized buffer cannot be used for DMA")]
146 ZeroSizedBuffer,
147}
148
149pub unsafe trait DmaPod: Copy {}
157
158unsafe impl<T: Copy> DmaPod for T {}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub struct DmaAllocHandle {
162 pub(crate) cpu_addr: NonNull<u8>,
163 pub(crate) dma_addr: DmaAddr,
164 pub(crate) layout: Layout,
165}
166
167impl DmaAllocHandle {
168 pub unsafe fn new(cpu_addr: NonNull<u8>, dma_addr: DmaAddr, layout: Layout) -> Self {
173 Self {
174 cpu_addr,
175 dma_addr,
176 layout,
177 }
178 }
179
180 pub fn size(&self) -> usize {
181 self.layout.size()
182 }
183
184 pub fn align(&self) -> usize {
185 self.layout.align()
186 }
187
188 pub fn as_ptr(&self) -> NonNull<u8> {
189 self.cpu_addr
190 }
191
192 pub fn dma_addr(&self) -> DmaAddr {
193 self.dma_addr
194 }
195
196 pub fn layout(&self) -> Layout {
197 self.layout
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
202pub struct DmaMapHandle {
203 pub(crate) cpu_addr: NonNull<u8>,
204 pub(crate) dma_addr: DmaAddr,
205 pub(crate) layout: Layout,
206 pub(crate) bounce_ptr: Option<NonNull<u8>>,
207}
208
209impl DmaMapHandle {
210 pub unsafe fn new(
216 cpu_addr: NonNull<u8>,
217 dma_addr: DmaAddr,
218 layout: Layout,
219 bounce_ptr: Option<NonNull<u8>>,
220 ) -> Self {
221 Self {
222 cpu_addr,
223 dma_addr,
224 layout,
225 bounce_ptr,
226 }
227 }
228
229 pub fn size(&self) -> usize {
230 self.layout.size()
231 }
232
233 pub fn align(&self) -> usize {
234 self.layout.align()
235 }
236
237 pub fn as_ptr(&self) -> NonNull<u8> {
238 self.cpu_addr
239 }
240
241 pub fn dma_addr(&self) -> DmaAddr {
242 self.dma_addr
243 }
244
245 pub fn layout(&self) -> Layout {
246 self.layout
247 }
248
249 pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
250 self.bounce_ptr
251 }
252}