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 #[error("DMA coherent allocation could not be released and was quarantined")]
148 CoherentReleaseFailed,
149}
150
151pub unsafe trait DmaPod: Copy {}
159
160unsafe impl<T: Copy> DmaPod for T {}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub struct DmaAllocHandle {
164 pub(crate) cpu_addr: NonNull<u8>,
165 pub(crate) dma_addr: DmaAddr,
166 pub(crate) layout: Layout,
167}
168
169impl DmaAllocHandle {
170 pub unsafe fn new(cpu_addr: NonNull<u8>, dma_addr: DmaAddr, layout: Layout) -> Self {
175 Self {
176 cpu_addr,
177 dma_addr,
178 layout,
179 }
180 }
181
182 pub fn size(&self) -> usize {
183 self.layout.size()
184 }
185
186 pub fn align(&self) -> usize {
187 self.layout.align()
188 }
189
190 pub fn as_ptr(&self) -> NonNull<u8> {
191 self.cpu_addr
192 }
193
194 pub fn dma_addr(&self) -> DmaAddr {
195 self.dma_addr
196 }
197
198 pub fn layout(&self) -> Layout {
199 self.layout
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
204pub struct DmaMapHandle {
205 pub(crate) cpu_addr: NonNull<u8>,
206 pub(crate) dma_addr: DmaAddr,
207 pub(crate) layout: Layout,
208 pub(crate) bounce_ptr: Option<NonNull<u8>>,
209}
210
211impl DmaMapHandle {
212 pub unsafe fn new(
218 cpu_addr: NonNull<u8>,
219 dma_addr: DmaAddr,
220 layout: Layout,
221 bounce_ptr: Option<NonNull<u8>>,
222 ) -> Self {
223 Self {
224 cpu_addr,
225 dma_addr,
226 layout,
227 bounce_ptr,
228 }
229 }
230
231 pub fn size(&self) -> usize {
232 self.layout.size()
233 }
234
235 pub fn align(&self) -> usize {
236 self.layout.align()
237 }
238
239 pub fn as_ptr(&self) -> NonNull<u8> {
240 self.cpu_addr
241 }
242
243 pub fn dma_addr(&self) -> DmaAddr {
244 self.dma_addr
245 }
246
247 pub fn layout(&self) -> Layout {
248 self.layout
249 }
250
251 pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
252 self.bounce_ptr
253 }
254}