1use core::{alloc::Layout, marker::PhantomData, ops::Range, ptr::NonNull};
2
3use crate::{
4 DeviceDma, DmaAddr, DmaCoherency, DmaDirection, DmaDomainId, DmaError, DmaPod,
5 common::{AllocationKind, DmaAllocation},
6};
7
8pub struct CoherentArray<T: DmaPod> {
9 data: DmaAllocation,
10 _phantom: PhantomData<T>,
11}
12
13unsafe impl<T: DmaPod + Send> Send for CoherentArray<T> {}
14unsafe impl<T: DmaPod + Sync> Sync for CoherentArray<T> {}
15
16impl<T: DmaPod> CoherentArray<T> {
17 pub(crate) fn new_zero_with_align(
18 os: &DeviceDma,
19 len: usize,
20 align: usize,
21 ) -> Result<Self, DmaError> {
22 let layout = array_layout::<T>(len, align)?;
23 Ok(Self {
24 data: DmaAllocation::new_zero_coherent(os, layout)?,
25 _phantom: PhantomData,
26 })
27 }
28
29 pub(crate) fn new_zero(os: &DeviceDma, len: usize) -> Result<Self, DmaError> {
30 Self::new_zero_with_align(os, len, core::mem::align_of::<T>())
31 }
32
33 pub fn dma_addr(&self) -> DmaAddr {
34 self.data.handle().dma_addr()
35 }
36
37 pub fn len(&self) -> usize {
38 len_from_bytes::<T>(self.data.handle().size())
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.len() == 0
43 }
44
45 pub fn bytes_len(&self) -> usize {
46 self.data.handle().size()
47 }
48
49 pub fn read_cpu(&self, index: usize) -> Option<T> {
50 read_at(self.as_ptr(), self.len(), index)
51 }
52
53 pub fn set_cpu(&mut self, index: usize, value: T) {
54 write_at(self.as_ptr(), self.len(), index, value);
55 }
56
57 pub fn copy_from_slice_cpu(&mut self, src: &[T]) {
58 copy_from_slice(self.as_ptr(), self.len(), src);
59 }
60
61 pub fn iter_cpu(&self) -> ArrayCpuIter<'_, T, Self> {
62 ArrayCpuIter {
63 array: self,
64 index: 0,
65 _phantom: PhantomData,
66 }
67 }
68
69 pub fn write_with_cpu<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
70 assert!(len <= self.len(), "range out of bounds");
71 let data = unsafe { self.as_mut_slice_cpu() };
72 f(&mut data[..len])
73 }
74
75 pub fn read_with_cpu<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
76 assert!(len <= self.len(), "range out of bounds");
77 let data = unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), len) };
78 f(data)
79 }
80
81 pub fn as_ptr(&self) -> NonNull<T> {
82 self.data.handle().as_ptr().cast::<T>()
83 }
84
85 pub fn as_slice_cpu(&self) -> &[T] {
86 unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), self.len()) }
87 }
88
89 pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [T] {
94 unsafe { core::slice::from_raw_parts_mut(self.as_ptr().as_ptr(), self.len()) }
95 }
96
97 pub fn try_release(mut self) -> Result<(), DmaError> {
98 self.data.try_release()
99 }
100}
101
102pub struct ContiguousArray<T: DmaPod> {
103 data: DmaAllocation,
104 _phantom: PhantomData<T>,
105}
106
107unsafe impl<T: DmaPod + Send> Send for ContiguousArray<T> {}
108unsafe impl<T: DmaPod + Sync> Sync for ContiguousArray<T> {}
109
110impl<T: DmaPod> ContiguousArray<T> {
111 pub(crate) fn new_zero_with_align(
112 os: &DeviceDma,
113 len: usize,
114 align: usize,
115 direction: DmaDirection,
116 ) -> Result<Self, DmaError> {
117 let layout = array_layout::<T>(len, align)?;
118 Ok(Self {
119 data: DmaAllocation::new_zero_contiguous(os, layout, direction)?,
120 _phantom: PhantomData,
121 })
122 }
123
124 pub(crate) fn new_zero(
125 os: &DeviceDma,
126 len: usize,
127 direction: DmaDirection,
128 ) -> Result<Self, DmaError> {
129 Self::new_zero_with_align(os, len, core::mem::align_of::<T>(), direction)
130 }
131
132 pub fn dma_addr(&self) -> DmaAddr {
133 self.data.handle().dma_addr()
134 }
135
136 pub fn len(&self) -> usize {
137 len_from_bytes::<T>(self.data.handle().size())
138 }
139
140 pub fn is_empty(&self) -> bool {
141 self.len() == 0
142 }
143
144 pub fn bytes_len(&self) -> usize {
145 self.data.handle().size()
146 }
147
148 pub fn domain_id(&self) -> DmaDomainId {
149 self.data.device.info().domain()
150 }
151
152 pub fn coherency(&self) -> DmaCoherency {
153 self.data.device.info().coherency()
154 }
155
156 pub fn direction(&self) -> DmaDirection {
157 match self.data.kind {
158 AllocationKind::Contiguous { direction } => direction,
159 AllocationKind::Coherent => unreachable!("ContiguousArray cannot hold coherent DMA"),
160 }
161 }
162
163 pub fn read_cpu(&self, index: usize) -> Option<T> {
164 read_at(self.as_ptr(), self.len(), index)
165 }
166
167 pub fn set_cpu(&mut self, index: usize, value: T) {
168 write_at(self.as_ptr(), self.len(), index, value);
169 }
170
171 pub fn copy_from_slice_cpu(&mut self, src: &[T]) {
172 copy_from_slice(self.as_ptr(), self.len(), src);
173 }
174
175 pub fn iter_cpu(&self) -> ArrayCpuIter<'_, T, Self> {
176 ArrayCpuIter {
177 array: self,
178 index: 0,
179 _phantom: PhantomData,
180 }
181 }
182
183 pub fn prepare_for_device(&self, range: Range<usize>) {
184 self.check_range(&range);
185 self.data.sync_for_device(range.start, range.len());
186 }
187
188 pub fn complete_for_cpu(&self, range: Range<usize>) {
189 self.check_range(&range);
190 self.data.sync_for_cpu(range.start, range.len());
191 }
192
193 pub fn write_for_device<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
194 let ret = self.write_with_cpu(len, f);
195 self.prepare_for_device(0..len * core::mem::size_of::<T>());
196 ret
197 }
198
199 pub fn read_from_device<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
200 let size = len * core::mem::size_of::<T>();
201 self.complete_for_cpu(0..size);
202 self.read_with_cpu(len, f)
203 }
204
205 pub fn copy_to_device_from_slice(&mut self, src: &[T]) {
206 self.copy_from_slice_cpu(src);
207 self.prepare_for_device(0..core::mem::size_of_val(src));
208 }
209
210 pub fn copy_from_device_to_slice(&self, dst: &mut [T]) {
211 self.read_from_device(dst.len(), |src| dst.copy_from_slice(src));
212 }
213
214 pub fn write_with_cpu<R>(&mut self, len: usize, f: impl FnOnce(&mut [T]) -> R) -> R {
215 assert!(len <= self.len(), "range out of bounds");
216 {
217 let data = unsafe { self.as_mut_slice_cpu() };
218 f(&mut data[..len])
219 }
220 }
221
222 pub fn read_with_cpu<R>(&self, len: usize, f: impl FnOnce(&[T]) -> R) -> R {
223 assert!(len <= self.len(), "range out of bounds");
224 let data = unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), len) };
225 f(data)
226 }
227
228 pub fn as_ptr(&self) -> NonNull<T> {
229 self.data.handle().as_ptr().cast::<T>()
230 }
231
232 pub fn as_slice_cpu(&self) -> &[T] {
233 unsafe { core::slice::from_raw_parts(self.as_ptr().as_ptr(), self.len()) }
234 }
235
236 pub unsafe fn as_mut_slice_cpu(&mut self) -> &mut [T] {
241 unsafe { core::slice::from_raw_parts_mut(self.as_ptr().as_ptr(), self.len()) }
242 }
243
244 fn check_range(&self, range: &Range<usize>) {
245 assert!(
246 range.start <= range.end && range.end <= self.bytes_len(),
247 "range out of bounds, range: {:?}, bytes_len: {}",
248 range,
249 self.bytes_len()
250 );
251 }
252}
253
254pub trait DmaArrayCpuRead<T: DmaPod> {
255 fn len(&self) -> usize;
256 fn is_empty(&self) -> bool;
257 fn read_cpu(&self, index: usize) -> Option<T>;
258}
259
260impl<T: DmaPod> DmaArrayCpuRead<T> for CoherentArray<T> {
261 fn len(&self) -> usize {
262 CoherentArray::len(self)
263 }
264
265 fn is_empty(&self) -> bool {
266 CoherentArray::is_empty(self)
267 }
268
269 fn read_cpu(&self, index: usize) -> Option<T> {
270 CoherentArray::read_cpu(self, index)
271 }
272}
273
274impl<T: DmaPod> DmaArrayCpuRead<T> for ContiguousArray<T> {
275 fn len(&self) -> usize {
276 ContiguousArray::len(self)
277 }
278
279 fn is_empty(&self) -> bool {
280 ContiguousArray::is_empty(self)
281 }
282
283 fn read_cpu(&self, index: usize) -> Option<T> {
284 ContiguousArray::read_cpu(self, index)
285 }
286}
287
288pub struct ArrayCpuIter<'a, T: DmaPod, A: DmaArrayCpuRead<T>> {
289 array: &'a A,
290 index: usize,
291 _phantom: PhantomData<T>,
292}
293
294impl<'a, T: DmaPod, A: DmaArrayCpuRead<T>> Iterator for ArrayCpuIter<'a, T, A> {
295 type Item = T;
296
297 fn next(&mut self) -> Option<Self::Item> {
298 if self.index >= self.array.len() {
299 return None;
300 }
301 let value = self.array.read_cpu(self.index);
302 self.index += 1;
303 value
304 }
305}
306
307fn array_layout<T>(len: usize, align: usize) -> Result<Layout, DmaError> {
308 let size = len
309 .checked_mul(core::mem::size_of::<T>())
310 .ok_or(DmaError::LayoutError(
311 Layout::from_size_align(usize::MAX, 1).unwrap_err(),
312 ))?;
313 Ok(Layout::from_size_align(
314 size,
315 align.max(core::mem::align_of::<T>()),
316 )?)
317}
318
319fn len_from_bytes<T>(bytes: usize) -> usize {
320 if core::mem::size_of::<T>() == 0 {
321 0
322 } else {
323 bytes / core::mem::size_of::<T>()
324 }
325}
326
327fn read_at<T: DmaPod>(ptr: NonNull<T>, len: usize, index: usize) -> Option<T> {
328 if index >= len {
329 return None;
330 }
331 Some(unsafe { ptr.add(index).read() })
332}
333
334fn write_at<T: DmaPod>(ptr: NonNull<T>, len: usize, index: usize, value: T) {
335 assert!(
336 index < len,
337 "index out of range, index: {}, len: {}",
338 index,
339 len
340 );
341 unsafe { ptr.add(index).write(value) };
342}
343
344fn copy_from_slice<T: DmaPod>(ptr: NonNull<T>, len: usize, src: &[T]) {
345 assert!(
346 src.len() <= len,
347 "source slice is larger than DMA array, src len: {}, array len: {}",
348 src.len(),
349 len
350 );
351 unsafe {
352 ptr.as_ptr()
353 .copy_from_nonoverlapping(src.as_ptr(), src.len());
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 fn array_helper_len_and_layout_rules_hold_for_test() -> bool {
362 assert!(len_from_bytes::<u8>(100) == 100);
364 assert!(len_from_bytes::<u16>(100) == 50);
365 assert!(len_from_bytes::<u32>(100) == 25);
366 assert!(len_from_bytes::<u64>(100) == 12);
367
368 let layout = array_layout::<u8>(100, 1);
370 assert!(layout.is_ok());
371 let l = layout.unwrap();
372 assert!(l.size() == 100);
373
374 let overflow = array_layout::<u8>(usize::MAX, 1);
376 assert!(overflow.is_err());
377
378 let bad_align = array_layout::<u8>(10, 3); assert!(bad_align.is_err());
381
382 assert!(len_from_bytes::<u32>(0) == 0);
384
385 let empty = array_layout::<u32>(0, 4);
387 assert!(empty.is_ok());
388 assert!(empty.unwrap().size() == 0);
389
390 assert!(len_from_bytes::<u16>(5) == 2); let large_align = array_layout::<u8>(16, 4096); assert!(large_align.is_ok());
396 assert!(large_align.unwrap().align() == 4096);
397
398 true
399 }
400
401 fn array_contiguous_methods_hold_for_test() -> bool {
402 assert!(array_helper_len_and_layout_rules_hold_for_test());
405
406 assert!(len_from_bytes::<u8>(100) == 100);
408 assert!(len_from_bytes::<u16>(100) == 50);
409 assert!(len_from_bytes::<u32>(100) == 25);
410 assert!(len_from_bytes::<u64>(100) == 12);
411
412 true
413 }
414
415 fn array_read_at_write_at_helpers_hold_for_test() -> bool {
416 assert!(len_from_bytes::<u8>(0) == 0);
419 assert!(len_from_bytes::<u32>(0) == 0);
420
421 let empty = array_layout::<u8>(0, 1);
423 assert!(empty.is_ok());
424 assert!(empty.unwrap().size() == 0);
425
426 true
427 }
428
429 fn array_dma_array_cpu_read_trait_hold_for_test() -> bool {
430 assert!(len_from_bytes::<u8>(100) == 100);
433 assert!(len_from_bytes::<u16>(50) == 25);
434
435 true
436 }
437
438 fn array_layout_edge_cases_comprehensive_hold_for_test() -> bool {
439 assert!(len_from_bytes::<()>(100) == 0);
443
444 let tiny = array_layout::<u8>(1, 1);
446 assert!(tiny.is_ok());
447 assert_eq!(tiny.unwrap().size(), 1);
448
449 assert!(array_layout::<u8>(10, 3).is_err());
451
452 assert!(array_layout::<u16>(5, 2).is_ok());
454
455 assert_eq!(len_from_bytes::<u32>(40), 10); assert_eq!(len_from_bytes::<u32>(42), 10); true
462 }
463
464 fn array_copy_from_slice_and_write_at_edge_hold_for_test() -> bool {
465 assert_eq!(len_from_bytes::<u8>(0), 0);
469 assert_eq!(len_from_bytes::<u8>(1), 1);
470 assert_eq!(len_from_bytes::<u8>(255), 255);
471
472 assert_eq!(len_from_bytes::<u16>(0), 0);
474 assert_eq!(len_from_bytes::<u16>(2), 1);
475 assert_eq!(len_from_bytes::<u16>(3), 1); assert_eq!(len_from_bytes::<u16>(4), 2);
477
478 assert_eq!(len_from_bytes::<u64>(0), 0);
480 assert_eq!(len_from_bytes::<u64>(7), 0); assert_eq!(len_from_bytes::<u64>(8), 1);
482 assert_eq!(len_from_bytes::<u64>(15), 1); assert_eq!(len_from_bytes::<u64>(16), 2);
484
485 assert!(array_layout::<u8>(0, 1).is_ok());
488 assert!(array_layout::<u8>(1, 1).is_ok());
489 assert!(array_layout::<u8>(256, 1).is_ok());
490
491 assert!(array_layout::<u16>(1, 2).is_ok()); assert!(array_layout::<u16>(100, 2).is_ok()); assert!(array_layout::<u32>(10, 4).is_ok()); assert!(array_layout::<u32>(0, 4).is_ok()); assert!(array_layout::<u64>(5, 8).is_ok()); assert!(array_layout::<u8>(10, 3).is_err()); assert!(array_layout::<u8>(10, 5).is_err()); assert!(array_layout::<u8>(10, 6).is_err()); assert!(array_layout::<u8>(10, 7).is_err()); assert!(array_layout::<u8>(10, 9).is_err()); true
510 }
511
512 fn array_layout_overflow_and_size_align_hold_for_test() -> bool {
513 let overflow_u8 = array_layout::<u8>(usize::MAX, 1);
517 assert!(overflow_u8.is_err());
518
519 let overflow_u16 = array_layout::<u16>(usize::MAX / 2 + 1, 2);
520 assert!(overflow_u16.is_err());
521
522 let overflow_u32 = array_layout::<u32>(usize::MAX / 4 + 1, 4);
523 assert!(overflow_u32.is_err());
524
525 let large = array_layout::<u8>(1024 * 1024, 4096);
527 assert!(large.is_ok());
528 let l = large.unwrap();
529 assert_eq!(l.size(), 1024 * 1024);
530 assert_eq!(l.align(), 4096);
531
532 assert!(array_layout::<u8>(0, 1).is_ok());
534 assert!(array_layout::<u8>(0, 2).is_ok());
535 assert!(array_layout::<u8>(0, 4).is_ok());
536 assert!(array_layout::<u8>(0, 8).is_ok());
537 assert!(array_layout::<u8>(0, 16).is_ok());
538 assert!(array_layout::<u8>(0, 32).is_ok());
539 assert!(array_layout::<u8>(0, 64).is_ok());
540 assert!(array_layout::<u8>(0, 128).is_ok());
541 assert!(array_layout::<u8>(0, 256).is_ok());
542 assert!(array_layout::<u8>(0, 512).is_ok());
543 assert!(array_layout::<u8>(0, 1024).is_ok());
544 assert!(array_layout::<u8>(0, 2048).is_ok());
545 assert!(array_layout::<u8>(0, 4096).is_ok());
546
547 let a1 = array_layout::<u8>(10, 16).unwrap();
550 assert_eq!(a1.align(), 16);
551
552 let a2 = array_layout::<u16>(10, 1).unwrap();
554 assert_eq!(a2.align(), 2); true
557 }
558
559 #[test]
560 fn array_helper_len_and_layout_rules_hold() {
561 assert!(array_helper_len_and_layout_rules_hold_for_test());
562 }
563
564 #[test]
565 fn array_contiguous_methods_hold() {
566 assert!(array_contiguous_methods_hold_for_test());
567 }
568
569 #[test]
570 fn array_read_at_write_at_helpers_hold() {
571 assert!(array_read_at_write_at_helpers_hold_for_test());
572 }
573
574 #[test]
575 fn array_dma_array_cpu_read_trait_hold() {
576 assert!(array_dma_array_cpu_read_trait_hold_for_test());
577 }
578
579 #[test]
580 fn array_layout_edge_cases_comprehensive_hold() {
581 assert!(array_layout_edge_cases_comprehensive_hold_for_test());
582 }
583
584 #[test]
585 fn array_copy_from_slice_and_write_at_edge_hold() {
586 assert!(array_copy_from_slice_and_write_at_edge_hold_for_test());
587 }
588
589 #[test]
590 fn array_layout_overflow_and_size_align_hold() {
591 assert!(array_layout_overflow_and_size_align_hold_for_test());
592 }
593}