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 enum DmaDomainId {
58 Direct,
60 Translated(NonZeroU64),
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct DmaConstraints {
67 pub addr_mask: u64,
68 pub align: usize,
69 pub boundary: Option<usize>,
70 pub max_segment_size: Option<usize>,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78pub enum DmaCoherency {
79 Coherent,
82 NonCoherent,
85}
86
87impl DmaConstraints {
88 pub const fn new(addr_mask: u64) -> Self {
89 Self {
90 addr_mask,
91 align: 1,
92 boundary: None,
93 max_segment_size: None,
94 }
95 }
96
97 pub const fn with_align(mut self, align: usize) -> Self {
98 self.align = if align == 0 { 1 } else { align };
99 self
100 }
101
102 pub const fn with_boundary(mut self, boundary: usize) -> Self {
103 self.boundary = Some(if boundary == 0 { 1 } else { boundary });
104 self
105 }
106
107 pub const fn with_max_segment_size(mut self, max_segment_size: usize) -> Self {
108 self.max_segment_size = Some(max_segment_size);
109 self
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct DmaDeviceInfo {
119 domain: DmaDomainId,
120 coherency: DmaCoherency,
121 constraints: DmaConstraints,
122}
123
124impl DmaDeviceInfo {
125 pub const fn new(
126 domain: DmaDomainId,
127 coherency: DmaCoherency,
128 constraints: DmaConstraints,
129 ) -> Self {
130 Self {
131 domain,
132 coherency,
133 constraints,
134 }
135 }
136
137 pub const fn domain(self) -> DmaDomainId {
138 self.domain
139 }
140
141 pub const fn coherency(self) -> DmaCoherency {
142 self.coherency
143 }
144
145 pub const fn constraints(self) -> DmaConstraints {
146 self.constraints
147 }
148
149 pub const fn with_constraints(self, constraints: DmaConstraints) -> Self {
150 Self {
151 constraints,
152 ..self
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159pub enum DmaDirection {
160 ToDevice,
162 FromDevice,
164 Bidirectional,
166}
167
168#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
169pub enum DmaError {
170 #[error("DMA allocation failed")]
171 NoMemory,
172 #[error("Invalid layout")]
173 LayoutError(#[from] core::alloc::LayoutError),
174 #[error("DMA address {addr} does not match device mask {mask:#X}")]
175 DmaMaskNotMatch { addr: DmaAddr, mask: u64 },
176 #[error("DMA align mismatch: required={required:#X}, but address={address}")]
177 AlignMismatch { required: usize, address: DmaAddr },
178 #[error("DMA segment size {size:#X} exceeds max segment size {max:#X}")]
179 SegmentTooLarge { size: usize, max: usize },
180 #[error("DMA address range crosses boundary {boundary:#X}: addr={addr}, size={size:#X}")]
181 BoundaryCross {
182 addr: DmaAddr,
183 size: usize,
184 boundary: usize,
185 },
186 #[error("Null pointer provided for DMA mapping")]
187 NullPointer,
188 #[error("Zero-sized buffer cannot be used for DMA")]
189 ZeroSizedBuffer,
190 #[error("DMA coherent allocation could not be released and was quarantined")]
191 CoherentReleaseFailed,
192}
193
194pub unsafe trait DmaPod: Copy {}
202
203unsafe impl<T: Copy> DmaPod for T {}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
206pub struct DmaAllocHandle {
207 pub(crate) cpu_addr: NonNull<u8>,
208 pub(crate) allocation_addr: NonNull<u8>,
209 pub(crate) dma_addr: DmaAddr,
210 pub(crate) layout: Layout,
211}
212
213impl DmaAllocHandle {
214 pub unsafe fn new(
223 cpu_addr: NonNull<u8>,
224 allocation_addr: NonNull<u8>,
225 dma_addr: DmaAddr,
226 layout: Layout,
227 ) -> Self {
228 Self {
229 cpu_addr,
230 allocation_addr,
231 dma_addr,
232 layout,
233 }
234 }
235
236 pub fn size(&self) -> usize {
237 self.layout.size()
238 }
239
240 pub fn align(&self) -> usize {
241 self.layout.align()
242 }
243
244 pub fn as_ptr(&self) -> NonNull<u8> {
245 self.cpu_addr
246 }
247
248 pub fn allocation_ptr(&self) -> NonNull<u8> {
251 self.allocation_addr
252 }
253
254 pub fn dma_addr(&self) -> DmaAddr {
255 self.dma_addr
256 }
257
258 pub fn layout(&self) -> Layout {
259 self.layout
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
264pub struct DmaMapHandle {
265 pub(crate) cpu_addr: NonNull<u8>,
266 pub(crate) dma_addr: DmaAddr,
267 pub(crate) layout: Layout,
268 pub(crate) bounce_ptr: Option<NonNull<u8>>,
269}
270
271impl DmaMapHandle {
272 pub unsafe fn new(
278 cpu_addr: NonNull<u8>,
279 dma_addr: DmaAddr,
280 layout: Layout,
281 bounce_ptr: Option<NonNull<u8>>,
282 ) -> Self {
283 Self {
284 cpu_addr,
285 dma_addr,
286 layout,
287 bounce_ptr,
288 }
289 }
290
291 pub fn size(&self) -> usize {
292 self.layout.size()
293 }
294
295 pub fn align(&self) -> usize {
296 self.layout.align()
297 }
298
299 pub fn as_ptr(&self) -> NonNull<u8> {
300 self.cpu_addr
301 }
302
303 pub fn dma_addr(&self) -> DmaAddr {
304 self.dma_addr
305 }
306
307 pub fn layout(&self) -> Layout {
308 self.layout
309 }
310
311 pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
312 self.bounce_ptr
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn coherent_handle_keeps_cpu_alias_and_allocator_address_distinct() {
322 let alias = NonNull::new(0x8000_usize as *mut u8).unwrap();
323 let allocation = NonNull::new(0x4000_usize as *mut u8).unwrap();
324 let layout = Layout::from_size_align(0x1000, 0x1000).unwrap();
325 let handle = unsafe { DmaAllocHandle::new(alias, allocation, 0x2000_u64.into(), layout) };
326
327 assert_eq!(handle.as_ptr(), alias);
328 assert_eq!(handle.allocation_ptr(), allocation);
329 assert_eq!(handle.dma_addr(), DmaAddr::from(0x2000_u64));
330 }
331}