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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum DmaDirection {
90 ToDevice,
92 FromDevice,
94 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
122pub 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 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 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}