Skip to main content

dma_api/
streaming.rs

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        // Verify StreamingMap struct exists with PhantomData marker
163        // We can't construct it without a real DeviceDma, but verify type properties
164
165        // Check that size_of::<T>() == 0 gives len() == 0
166        assert!(core::mem::size_of::<u8>() > 0);
167
168        true
169    }
170
171    fn streaming_direction_and_error_types_hold_for_test() -> bool {
172        // Test DmaDirection variants
173        use crate::DmaDirection;
174        let _to_device = DmaDirection::ToDevice;
175        let _from_device = DmaDirection::FromDevice;
176        let _bidirectional = DmaDirection::Bidirectional;
177
178        // Test DmaError variants
179        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        // Test that StreamingMap has expected size properties
187        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        // Test all DmaError variants
196        use crate::DmaError;
197
198        let _no_memory = DmaError::NoMemory;
199        let _null_pointer = DmaError::NullPointer;
200        let _zero_sized = DmaError::ZeroSizedBuffer;
201
202        // Verify they are different types
203        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        // Test that common types satisfy DmaPod bounds
210        use core::mem;
211
212        // u8 is POD
213        assert!(mem::size_of::<u8>() == 1);
214        assert!(mem::align_of::<u8>() >= 1);
215
216        // u32 is POD
217        assert!(mem::size_of::<u32>() == 4);
218        assert!(mem::align_of::<u32>() >= 1);
219
220        // u64 is POD
221        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        // Test NonZeroUsize
231        let nz = NonZeroUsize::new(42).unwrap();
232        assert_eq!(nz.get(), 42);
233
234        // Test PhantomData
235        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        // Test all DmaDirection variants
244        let to_device = DmaDirection::ToDevice;
245        let from_device = DmaDirection::FromDevice;
246        let bidirectional = DmaDirection::Bidirectional;
247
248        // Verify they are different
249        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}