Skip to main content

dma_api/
def.rs

1use core::{alloc::Layout, cmp::PartialOrd, 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/// Device-visible DMA constraints.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct DmaConstraints {
55    pub addr_mask: u64,
56    pub align: usize,
57    pub boundary: Option<usize>,
58    pub max_segment_size: Option<usize>,
59}
60
61impl DmaConstraints {
62    pub const fn new(addr_mask: u64) -> Self {
63        Self {
64            addr_mask,
65            align: 1,
66            boundary: None,
67            max_segment_size: None,
68        }
69    }
70
71    pub fn with_align(mut self, align: usize) -> Self {
72        self.align = align.max(1);
73        self
74    }
75
76    pub fn with_boundary(mut self, boundary: usize) -> Self {
77        self.boundary = Some(boundary.max(1));
78        self
79    }
80
81    pub fn with_max_segment_size(mut self, max_segment_size: usize) -> Self {
82        self.max_segment_size = Some(max_segment_size);
83        self
84    }
85}
86
87/// DMA transfer direction.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum DmaDirection {
90    /// CPU writes, device reads.
91    ToDevice,
92    /// Device writes, CPU reads.
93    FromDevice,
94    /// CPU and device may both read/write.
95    Bidirectional,
96}
97
98#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
99pub enum DmaError {
100    #[error("DMA allocation failed")]
101    NoMemory,
102    #[error("Invalid layout")]
103    LayoutError(#[from] core::alloc::LayoutError),
104    #[error("DMA address {addr} does not match device mask {mask:#X}")]
105    DmaMaskNotMatch { addr: DmaAddr, mask: u64 },
106    #[error("DMA align mismatch: required={required:#X}, but address={address}")]
107    AlignMismatch { required: usize, address: DmaAddr },
108    #[error("DMA segment size {size:#X} exceeds max segment size {max:#X}")]
109    SegmentTooLarge { size: usize, max: usize },
110    #[error("DMA address range crosses boundary {boundary:#X}: addr={addr}, size={size:#X}")]
111    BoundaryCross {
112        addr: DmaAddr,
113        size: usize,
114        boundary: usize,
115    },
116    #[error("Null pointer provided for DMA mapping")]
117    NullPointer,
118    #[error("Zero-sized buffer cannot be used for DMA")]
119    ZeroSizedBuffer,
120}
121
122/// Marker for plain data that can be safely stored in typed DMA buffers.
123///
124/// # Safety
125///
126/// Implementors must be `Copy`, have no invalid all-zero bit pattern, and must
127/// not own resources or references whose validity can be broken by raw device
128/// writes.
129pub unsafe trait DmaPod: Copy {}
130
131unsafe impl<T: Copy> DmaPod for T {}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
134pub struct DmaAllocHandle {
135    pub(crate) cpu_addr: NonNull<u8>,
136    pub(crate) dma_addr: DmaAddr,
137    pub(crate) layout: Layout,
138}
139
140impl DmaAllocHandle {
141    /// # Safety
142    ///
143    /// `cpu_addr` must point to a live allocation described by `layout`, and
144    /// `dma_addr` must be the device-visible address for that allocation.
145    pub unsafe fn new(cpu_addr: NonNull<u8>, dma_addr: DmaAddr, layout: Layout) -> Self {
146        Self {
147            cpu_addr,
148            dma_addr,
149            layout,
150        }
151    }
152
153    pub fn size(&self) -> usize {
154        self.layout.size()
155    }
156
157    pub fn align(&self) -> usize {
158        self.layout.align()
159    }
160
161    pub fn as_ptr(&self) -> NonNull<u8> {
162        self.cpu_addr
163    }
164
165    pub fn dma_addr(&self) -> DmaAddr {
166        self.dma_addr
167    }
168
169    pub fn layout(&self) -> Layout {
170        self.layout
171    }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
175pub struct DmaMapHandle {
176    pub(crate) cpu_addr: NonNull<u8>,
177    pub(crate) dma_addr: DmaAddr,
178    pub(crate) layout: Layout,
179    pub(crate) bounce_ptr: Option<NonNull<u8>>,
180}
181
182impl DmaMapHandle {
183    /// # Safety
184    ///
185    /// `cpu_addr` must point to the caller-owned mapped buffer for the mapping
186    /// lifetime. `bounce_ptr`, when present, must point to a live bounce buffer
187    /// described by `layout`.
188    pub unsafe fn new(
189        cpu_addr: NonNull<u8>,
190        dma_addr: DmaAddr,
191        layout: Layout,
192        bounce_ptr: Option<NonNull<u8>>,
193    ) -> Self {
194        Self {
195            cpu_addr,
196            dma_addr,
197            layout,
198            bounce_ptr,
199        }
200    }
201
202    pub fn size(&self) -> usize {
203        self.layout.size()
204    }
205
206    pub fn align(&self) -> usize {
207        self.layout.align()
208    }
209
210    pub fn as_ptr(&self) -> NonNull<u8> {
211        self.cpu_addr
212    }
213
214    pub fn dma_addr(&self) -> DmaAddr {
215        self.dma_addr
216    }
217
218    pub fn layout(&self) -> Layout {
219        self.layout
220    }
221
222    pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
223        self.bounce_ptr
224    }
225}