1use alloc::vec::Vec;
2use core::{marker::PhantomData, num::NonZeroUsize, ops::Range, ptr::NonNull};
3
4use crate::{DeviceDma, DmaDirection, DmaError, DmaMapHandle, DmaPod};
5
6pub struct StreamingMap<T: DmaPod> {
7 handle: DmaMapHandle,
8 device: DeviceDma,
9 direction: DmaDirection,
10 _marker: PhantomData<*mut T>,
11}
12
13unsafe impl<T: DmaPod + Send> Send for StreamingMap<T> {}
14
15impl<T: DmaPod> StreamingMap<T> {
16 pub(crate) fn map(
17 os: &DeviceDma,
18 buff: &mut [T],
19 align: usize,
20 direction: DmaDirection,
21 ) -> Result<Self, DmaError> {
22 let addr = NonNull::new(buff.as_mut_ptr().cast::<u8>()).ok_or(DmaError::NullPointer)?;
23 let size =
24 NonZeroUsize::new(core::mem::size_of_val(buff)).ok_or(DmaError::ZeroSizedBuffer)?;
25 let handle = unsafe { os.map_streaming(addr, size, align, direction)? };
26
27 Ok(Self {
28 handle,
29 device: os.clone(),
30 direction,
31 _marker: PhantomData,
32 })
33 }
34
35 pub fn dma_addr(&self) -> crate::DmaAddr {
36 self.handle.dma_addr()
37 }
38
39 pub fn len(&self) -> usize {
40 if core::mem::size_of::<T>() == 0 {
41 0
42 } else {
43 self.handle.size() / core::mem::size_of::<T>()
44 }
45 }
46
47 pub fn is_empty(&self) -> bool {
48 self.len() == 0
49 }
50
51 pub fn bytes_len(&self) -> usize {
52 self.handle.size()
53 }
54
55 pub fn read_cpu(&self, index: usize) -> Option<T> {
56 if index >= self.len() {
57 return None;
58 }
59 Some(unsafe { self.handle.as_ptr().cast::<T>().add(index).read() })
60 }
61
62 pub fn set_cpu(&mut self, index: usize, value: T) {
63 assert!(
64 index < self.len(),
65 "index out of range, index: {}, len: {}",
66 index,
67 self.len()
68 );
69 unsafe {
70 self.handle.as_ptr().cast::<T>().add(index).write(value);
71 }
72 }
73
74 pub fn copy_from_slice_cpu(&mut self, src: &[T]) {
75 assert!(
76 core::mem::size_of_val(src) <= self.handle.size(),
77 "source slice is larger than DMA buffer"
78 );
79 unsafe {
80 self.handle
81 .as_ptr()
82 .cast::<T>()
83 .as_ptr()
84 .copy_from_nonoverlapping(src.as_ptr(), src.len());
85 }
86 }
87
88 pub fn write_with_cpu<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
89 assert!(len <= self.len(), "range out of bounds");
90 let data = unsafe {
91 core::slice::from_raw_parts_mut(self.handle.as_ptr().cast::<T>().as_ptr(), len)
92 };
93 f(data)
94 }
95
96 pub fn read_with_cpu<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
97 assert!(len <= self.len(), "range out of bounds");
98 let data =
99 unsafe { core::slice::from_raw_parts(self.handle.as_ptr().cast::<T>().as_ptr(), len) };
100 f(data)
101 }
102
103 pub fn to_vec_cpu(&self) -> Vec<T> {
104 let mut vec: Vec<T> = Vec::with_capacity(self.len());
105 unsafe {
106 let src_ptr = self.handle.as_ptr().as_ptr().cast::<T>();
107 let dst_ptr = vec.as_mut_ptr();
108 dst_ptr.copy_from_nonoverlapping(src_ptr, self.len());
109 vec.set_len(self.len());
110 }
111 vec
112 }
113
114 pub fn prepare_for_device(&self, range: Range<usize>) {
115 self.check_range(&range);
116 self.device
117 .sync_map_for_device(&self.handle, range.start, range.len(), self.direction);
118 }
119
120 pub fn complete_for_cpu(&self, range: Range<usize>) {
121 self.check_range(&range);
122 self.device
123 .sync_map_for_cpu(&self.handle, range.start, range.len(), self.direction);
124 }
125
126 pub fn write_for_device<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
127 let ret = self.write_with_cpu(len, f);
128 self.prepare_for_device(0..len * core::mem::size_of::<T>());
129 ret
130 }
131
132 pub fn read_from_device<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
133 self.complete_for_cpu(0..len * core::mem::size_of::<T>());
134 self.read_with_cpu(len, f)
135 }
136
137 pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
138 self.handle.bounce_ptr()
139 }
140
141 fn check_range(&self, range: &Range<usize>) {
142 assert!(
143 range.start <= range.end && range.end <= self.bytes_len(),
144 "range out of bounds, range: {:?}, bytes_len: {}",
145 range,
146 self.bytes_len()
147 );
148 }
149}
150
151impl<T: DmaPod> Drop for StreamingMap<T> {
152 fn drop(&mut self) {
153 unsafe {
154 self.device.unmap_streaming(self.handle);
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 fn streaming_struct_and_phantom_hold_for_test() -> bool {
162 assert!(core::mem::size_of::<u8>() > 0);
167
168 true
169 }
170
171 fn streaming_direction_and_error_types_hold_for_test() -> bool {
172 use crate::DmaDirection;
174 let _to_device = DmaDirection::ToDevice;
175 let _from_device = DmaDirection::FromDevice;
176 let _bidirectional = DmaDirection::Bidirectional;
177
178 use crate::DmaError;
180 let _no_memory = DmaError::NoMemory;
181
182 true
183 }
184
185 fn streaming_struct_size_and_alignment_hold_for_test() -> bool {
186 assert!(core::mem::size_of::<u8>() == 1);
188 assert!(core::mem::size_of::<u32>() == 4);
189 assert!(core::mem::size_of::<u64>() == 8);
190
191 true
192 }
193
194 fn streaming_all_error_variants_hold_for_test() -> bool {
195 use crate::DmaError;
197
198 let _no_memory = DmaError::NoMemory;
199 let _null_pointer = DmaError::NullPointer;
200 let _zero_sized = DmaError::ZeroSizedBuffer;
201
202 assert!(core::mem::size_of_val(&DmaError::NoMemory) > 0);
204
205 true
206 }
207
208 fn streaming_dma_pod_types_hold_for_test() -> bool {
209 use core::mem;
211
212 assert!(mem::size_of::<u8>() == 1);
214 assert!(mem::align_of::<u8>() >= 1);
215
216 assert!(mem::size_of::<u32>() == 4);
218 assert!(mem::align_of::<u32>() >= 1);
219
220 assert!(mem::size_of::<u64>() == 8);
222 assert!(mem::align_of::<u64>() >= 1);
223
224 true
225 }
226
227 fn streaming_nonzero_and_phantom_hold_for_test() -> bool {
228 use core::{marker::PhantomData, num::NonZeroUsize};
229
230 let nz = NonZeroUsize::new(42).unwrap();
232 assert_eq!(nz.get(), 42);
233
234 let _phantom: PhantomData<*mut u8> = PhantomData;
236
237 true
238 }
239
240 fn streaming_dma_direction_all_variants_hold_for_test() -> bool {
241 use crate::DmaDirection;
242
243 let to_device = DmaDirection::ToDevice;
245 let from_device = DmaDirection::FromDevice;
246 let bidirectional = DmaDirection::Bidirectional;
247
248 assert!(core::mem::discriminant(&to_device) != core::mem::discriminant(&from_device));
250 assert!(core::mem::discriminant(&from_device) != core::mem::discriminant(&bidirectional));
251 assert!(core::mem::discriminant(&to_device) != core::mem::discriminant(&bidirectional));
252
253 true
254 }
255
256 #[test]
257 fn streaming_struct_and_phantom_hold() {
258 assert!(streaming_struct_and_phantom_hold_for_test());
259 }
260
261 #[test]
262 fn streaming_direction_and_error_types_hold() {
263 assert!(streaming_direction_and_error_types_hold_for_test());
264 }
265
266 #[test]
267 fn streaming_struct_size_and_alignment_hold() {
268 assert!(streaming_struct_size_and_alignment_hold_for_test());
269 }
270
271 #[test]
272 fn streaming_all_error_variants_hold() {
273 assert!(streaming_all_error_variants_hold_for_test());
274 }
275
276 #[test]
277 fn streaming_dma_pod_types_hold() {
278 assert!(streaming_dma_pod_types_hold_for_test());
279 }
280
281 #[test]
282 fn streaming_nonzero_and_phantom_hold() {
283 assert!(streaming_nonzero_and_phantom_hold_for_test());
284 }
285
286 #[test]
287 fn streaming_dma_direction_all_variants_hold() {
288 assert!(streaming_dma_direction_all_variants_hold_for_test());
289 }
290}