Skip to main content

dma_api/op/
mod.rs

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    /// Allocates a device-visible contiguous DMA address range.
21    ///
22    /// The returned CPU mapping is normal memory. Non-coherent platforms must
23    /// use `sync_alloc_for_device` and `sync_alloc_for_cpu` to transfer
24    /// ownership between CPU and device.
25    ///
26    /// # Safety
27    ///
28    /// Implementations must return a live allocation described by `layout`,
29    /// with a DMA address range satisfying `constraints`, and that allocation
30    /// must remain valid until `dealloc_contiguous`.
31    unsafe fn alloc_contiguous(
32        &self,
33        constraints: DmaConstraints,
34        layout: core::alloc::Layout,
35    ) -> Option<DmaAllocHandle>;
36
37    /// # Safety
38    ///
39    /// Must be paired with `alloc_contiguous`.
40    unsafe fn dealloc_contiguous(&self, handle: DmaAllocHandle);
41
42    /// Allocates coherent DMA memory.
43    ///
44    /// Coherent memory is CPU/device visible without explicit cache
45    /// maintenance. Ordering barriers are still the driver's responsibility.
46    ///
47    /// # Safety
48    ///
49    /// Implementations must return a live allocation described by `layout`,
50    /// with a DMA address range satisfying `constraints`, and with the backend's
51    /// coherent mapping policy applied until `dealloc_coherent`.
52    unsafe fn alloc_coherent(
53        &self,
54        constraints: DmaConstraints,
55        layout: core::alloc::Layout,
56    ) -> Option<DmaAllocHandle>;
57
58    /// # Safety
59    ///
60    /// Must be paired with `alloc_coherent`.
61    unsafe fn dealloc_coherent(&self, handle: DmaAllocHandle);
62
63    /// Maps an existing caller-owned buffer for streaming DMA.
64    ///
65    /// # Safety
66    ///
67    /// `addr..addr + size` must remain live until `unmap_streaming`, and CPU
68    /// access while the device owns the mapping must follow the sync contract.
69    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    /// # Safety
78    ///
79    /// Must be paired with `map_streaming`.
80    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}