Skip to main content

dma_api/
def.rs

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/// Stable identity for one DMA translation domain.
53///
54/// Drivers use this to reject already-prepared DMA buffers that were prepared
55/// for a different device/IOMMU domain.
56#[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    /// Compatibility domain for legacy callers that have not plumbed a
65    /// device/IOMMU-specific identity yet.
66    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/// Device-visible DMA constraints.
80#[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/// DMA transfer direction.
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub enum DmaDirection {
117    /// CPU writes, device reads.
118    ToDevice,
119    /// Device writes, CPU reads.
120    FromDevice,
121    /// CPU and device may both read/write.
122    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
149/// Marker for plain data that can be safely stored in typed DMA buffers.
150///
151/// # Safety
152///
153/// Implementors must be `Copy`, have no invalid all-zero bit pattern, and must
154/// not own resources or references whose validity can be broken by raw device
155/// writes.
156pub 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    /// # Safety
169    ///
170    /// `cpu_addr` must point to a live allocation described by `layout`, and
171    /// `dma_addr` must be the device-visible address for that allocation.
172    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    /// # Safety
211    ///
212    /// `cpu_addr` must point to the caller-owned mapped buffer for the mapping
213    /// lifetime. `bounce_ptr`, when present, must point to a live bounce buffer
214    /// described by `layout`.
215    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}