1use core::{num::NonZeroUsize, ptr::NonNull};
2
3use mbarrier::mb;
4
5use crate::{DmaAllocHandle, 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(
53 &self,
54 constraints: DmaConstraints,
55 layout: core::alloc::Layout,
56 ) -> Option<DmaAllocHandle>;
57
58 unsafe fn dealloc_coherent(&self, handle: DmaAllocHandle);
62
63 unsafe fn map_streaming(
70 &self,
71 constraints: DmaConstraints,
72 addr: NonNull<u8>,
73 size: NonZeroUsize,
74 direction: DmaDirection,
75 ) -> Result<DmaMapHandle, DmaError>;
76
77 unsafe fn unmap_streaming(&self, handle: DmaMapHandle);
81
82 fn flush(&self, addr: NonNull<u8>, size: usize) {
83 mb();
84 arch::flush(addr, size)
85 }
86
87 fn invalidate(&self, addr: NonNull<u8>, size: usize) {
88 arch::invalidate(addr, size);
89 mb();
90 }
91
92 fn flush_invalidate(&self, addr: NonNull<u8>, size: usize) {
93 mb();
94 arch::flush_invalidate(addr, size);
95 mb();
96 }
97
98 fn sync_alloc_for_device(
99 &self,
100 handle: &DmaAllocHandle,
101 offset: usize,
102 size: usize,
103 direction: DmaDirection,
104 ) {
105 if matches!(
106 direction,
107 DmaDirection::ToDevice | DmaDirection::Bidirectional
108 ) {
109 self.flush(unsafe { handle.as_ptr().add(offset) }, size);
110 } else if matches!(direction, DmaDirection::FromDevice) {
111 self.invalidate(unsafe { handle.as_ptr().add(offset) }, size);
112 }
113 }
114
115 fn sync_alloc_for_cpu(
116 &self,
117 handle: &DmaAllocHandle,
118 offset: usize,
119 size: usize,
120 direction: DmaDirection,
121 ) {
122 if matches!(
123 direction,
124 DmaDirection::FromDevice | DmaDirection::Bidirectional
125 ) {
126 self.invalidate(unsafe { handle.as_ptr().add(offset) }, size);
127 }
128 }
129
130 fn sync_map_for_device(
131 &self,
132 handle: &DmaMapHandle,
133 offset: usize,
134 size: usize,
135 direction: DmaDirection,
136 ) {
137 let source = unsafe { handle.as_ptr().add(offset) };
138 if let Some(map_virt) = handle.bounce_ptr()
139 && map_virt != handle.as_ptr()
140 {
141 let target = unsafe { map_virt.add(offset) };
142 if matches!(
143 direction,
144 DmaDirection::ToDevice | DmaDirection::Bidirectional
145 ) {
146 unsafe {
147 target
148 .as_ptr()
149 .copy_from_nonoverlapping(source.as_ptr(), size);
150 }
151 self.flush(target, size);
152 } else if matches!(direction, DmaDirection::FromDevice) {
153 self.invalidate(target, size);
154 }
155 return;
156 }
157
158 match direction {
159 DmaDirection::ToDevice => self.flush(source, size),
160 DmaDirection::FromDevice => self.invalidate(source, size),
161 DmaDirection::Bidirectional => self.flush_invalidate(source, size),
162 }
163 }
164
165 fn sync_map_for_cpu(
166 &self,
167 handle: &DmaMapHandle,
168 offset: usize,
169 size: usize,
170 direction: DmaDirection,
171 ) {
172 if !matches!(
173 direction,
174 DmaDirection::FromDevice | DmaDirection::Bidirectional
175 ) {
176 return;
177 }
178
179 let target = unsafe { handle.as_ptr().add(offset) };
180 if let Some(map_virt) = handle.bounce_ptr()
181 && map_virt != handle.as_ptr()
182 {
183 let source = unsafe { map_virt.add(offset) };
184 self.invalidate(source, size);
185 unsafe {
186 target
187 .as_ptr()
188 .copy_from_nonoverlapping(source.as_ptr(), size);
189 }
190 return;
191 }
192
193 self.invalidate(target, size);
194 }
195}