1use core::{num::NonZeroUsize, ptr::NonNull};
2
3use mbarrier::mb;
4
5use crate::{DmaAllocHandle, DmaCoherency, DmaConstraints, DmaDirection, DmaError, DmaMapHandle};
6
7cfg_if::cfg_if! {
8 if #[cfg(target_arch = "aarch64")] {
9 #[path = "aarch64.rs"]
10 pub mod arch;
11 } else{
12 #[path = "nop.rs"]
13 pub mod arch;
14 }
15}
16
17pub trait DmaOp: Sync + Send + 'static {
18 fn page_size(&self) -> usize;
19
20 unsafe fn alloc_contiguous(
32 &self,
33 constraints: DmaConstraints,
34 layout: core::alloc::Layout,
35 ) -> Option<DmaAllocHandle>;
36
37 unsafe fn dealloc_contiguous(&self, handle: DmaAllocHandle);
41
42 unsafe fn alloc_coherent(
56 &self,
57 constraints: DmaConstraints,
58 layout: core::alloc::Layout,
59 ) -> Option<DmaAllocHandle>;
60
61 unsafe fn dealloc_coherent(&self, handle: DmaAllocHandle) -> Result<(), DmaError>;
67
68 unsafe fn map_streaming(
75 &self,
76 constraints: DmaConstraints,
77 addr: NonNull<u8>,
78 size: NonZeroUsize,
79 direction: DmaDirection,
80 ) -> Result<DmaMapHandle, DmaError>;
81
82 unsafe fn unmap_streaming(&self, handle: DmaMapHandle);
86
87 fn flush(&self, addr: NonNull<u8>, size: usize) {
88 mb();
89 arch::flush(addr, size)
90 }
91
92 fn invalidate(&self, addr: NonNull<u8>, size: usize) {
93 arch::invalidate(addr, size);
94 mb();
95 }
96
97 fn flush_invalidate(&self, addr: NonNull<u8>, size: usize) {
98 mb();
99 arch::flush_invalidate(addr, size);
100 mb();
101 }
102
103 fn sync_alloc_for_device(
104 &self,
105 handle: &DmaAllocHandle,
106 offset: usize,
107 size: usize,
108 direction: DmaDirection,
109 ) {
110 if matches!(
111 direction,
112 DmaDirection::ToDevice | DmaDirection::Bidirectional
113 ) {
114 self.flush(unsafe { handle.as_ptr().add(offset) }, size);
115 } else if matches!(direction, DmaDirection::FromDevice) {
116 self.invalidate(unsafe { handle.as_ptr().add(offset) }, size);
117 }
118 }
119
120 fn sync_alloc_for_cpu(
121 &self,
122 handle: &DmaAllocHandle,
123 offset: usize,
124 size: usize,
125 direction: DmaDirection,
126 ) {
127 if matches!(
128 direction,
129 DmaDirection::FromDevice | DmaDirection::Bidirectional
130 ) {
131 self.invalidate(unsafe { handle.as_ptr().add(offset) }, size);
132 }
133 }
134
135 fn sync_map_for_device(
136 &self,
137 handle: &DmaMapHandle,
138 offset: usize,
139 size: usize,
140 direction: DmaDirection,
141 coherency: DmaCoherency,
142 ) {
143 let source = unsafe { handle.as_ptr().add(offset) };
144 if let Some(map_virt) = handle.bounce_ptr()
145 && map_virt != handle.as_ptr()
146 {
147 let target = unsafe { map_virt.add(offset) };
148 if matches!(
149 direction,
150 DmaDirection::ToDevice | DmaDirection::Bidirectional
151 ) {
152 unsafe {
153 target
154 .as_ptr()
155 .copy_from_nonoverlapping(source.as_ptr(), size);
156 }
157 if coherency == DmaCoherency::NonCoherent {
158 self.flush(target, size);
159 }
160 } else if matches!(direction, DmaDirection::FromDevice)
161 && coherency == DmaCoherency::NonCoherent
162 {
163 self.invalidate(target, size);
164 }
165 return;
166 }
167
168 if coherency == DmaCoherency::Coherent {
169 return;
170 }
171
172 match direction {
173 DmaDirection::ToDevice => self.flush(source, size),
174 DmaDirection::FromDevice => self.invalidate(source, size),
175 DmaDirection::Bidirectional => self.flush_invalidate(source, size),
176 }
177 }
178
179 fn sync_map_for_cpu(
180 &self,
181 handle: &DmaMapHandle,
182 offset: usize,
183 size: usize,
184 direction: DmaDirection,
185 coherency: DmaCoherency,
186 ) {
187 if !matches!(
188 direction,
189 DmaDirection::FromDevice | DmaDirection::Bidirectional
190 ) {
191 return;
192 }
193
194 let target = unsafe { handle.as_ptr().add(offset) };
195 if let Some(map_virt) = handle.bounce_ptr()
196 && map_virt != handle.as_ptr()
197 {
198 let source = unsafe { map_virt.add(offset) };
199 if coherency == DmaCoherency::NonCoherent {
200 self.invalidate(source, size);
201 }
202 unsafe {
203 target
204 .as_ptr()
205 .copy_from_nonoverlapping(source.as_ptr(), size);
206 }
207 return;
208 }
209
210 if coherency == DmaCoherency::NonCoherent {
211 self.invalidate(target, size);
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 fn dma_op_direction_matching_hold_for_test() -> bool {
219 use crate::DmaDirection;
221
222 let to_device = DmaDirection::ToDevice;
223 let from_device = DmaDirection::FromDevice;
224 let bidirectional = DmaDirection::Bidirectional;
225
226 assert!(to_device != from_device);
228 assert!(from_device != bidirectional);
229 assert!(to_device != bidirectional);
230
231 true
232 }
233
234 fn dma_op_constraints_and_error_types_hold_for_test() -> bool {
235 use crate::DmaError;
237
238 let _no_memory = DmaError::NoMemory;
240
241 true
242 }
243
244 fn dma_op_sync_direction_branches_hold_for_test() -> bool {
245 use crate::DmaDirection;
247
248 assert!(matches!(DmaDirection::ToDevice, DmaDirection::ToDevice));
250 assert!(!matches!(DmaDirection::ToDevice, DmaDirection::FromDevice));
251 assert!(!matches!(
252 DmaDirection::ToDevice,
253 DmaDirection::Bidirectional
254 ));
255
256 assert!(matches!(DmaDirection::FromDevice, DmaDirection::FromDevice));
258 assert!(!matches!(DmaDirection::FromDevice, DmaDirection::ToDevice));
259 assert!(!matches!(
260 DmaDirection::FromDevice,
261 DmaDirection::Bidirectional
262 ));
263
264 assert!(matches!(
266 DmaDirection::Bidirectional,
267 DmaDirection::Bidirectional
268 ));
269 assert!(!matches!(
270 DmaDirection::Bidirectional,
271 DmaDirection::ToDevice
272 ));
273 assert!(!matches!(
274 DmaDirection::Bidirectional,
275 DmaDirection::FromDevice
276 ));
277
278 assert!(matches!(
280 DmaDirection::ToDevice,
281 DmaDirection::ToDevice | DmaDirection::Bidirectional
282 ));
283 assert!(!matches!(
284 DmaDirection::FromDevice,
285 DmaDirection::ToDevice | DmaDirection::Bidirectional
286 ));
287 assert!(matches!(
288 DmaDirection::Bidirectional,
289 DmaDirection::ToDevice | DmaDirection::Bidirectional
290 ));
291
292 true
293 }
294
295 #[test]
296 fn dma_op_direction_matching_hold() {
297 assert!(dma_op_direction_matching_hold_for_test());
298 }
299
300 #[test]
301 fn dma_op_constraints_and_error_types_hold() {
302 assert!(dma_op_constraints_and_error_types_hold_for_test());
303 }
304
305 #[test]
306 fn dma_op_sync_direction_branches_hold() {
307 assert!(dma_op_sync_direction_branches_hold_for_test());
308 }
309}