Skip to main content

dma_api/
lib.rs

1#![cfg_attr(target_os = "none", no_std)]
2#![doc = include_str!("../README.md")]
3
4extern crate alloc;
5
6use core::{num::NonZeroUsize, ops::Deref, ptr::NonNull};
7
8mod osal;
9
10mod array;
11mod common;
12mod dbox;
13mod def;
14mod map_single;
15
16pub use array::*;
17pub use dbox::*;
18pub use def::*;
19pub use map_single::*;
20pub use osal::DmaOp;
21
22impl Deref for DmaHandle {
23    type Target = core::alloc::Layout;
24    fn deref(&self) -> &Self::Target {
25        &self.layout
26    }
27}
28
29/// DMA 设备操作接口。
30///
31/// `DeviceDma` 是用于执行 DMA 操作的主要入口点,封装了平台特定的
32/// `DmaOp` 实现,并提供了分配、映射和管理 DMA 内存的方法。
33///
34/// # 创建
35///
36/// 使用 [`DeviceDma::new()`] 创建实例,需要提供:
37/// - `dma_mask`: 设备可寻址的地址掩码(如 `0xFFFFFFFF` 表示 32 位设备)
38/// - `osal`: 实现 `DmaOp` trait 的平台抽象层
39///
40/// # 示例
41///
42/// ```rust,ignore
43/// use dma_api::DeviceDma;
44///
45/// let device = DeviceDma::new(0xFFFFFFFF, &my_dma_impl);
46/// ```
47#[derive(Clone)]
48pub struct DeviceDma {
49    os: &'static dyn DmaOp,
50    mask: u64,
51}
52
53impl DeviceDma {
54    /// 创建新的 DMA 设备实例。
55    ///
56    /// # 参数
57    ///
58    /// - `dma_mask`: 设备 DMA 地址掩码,指定设备可寻址的地址范围
59    ///   - `0xFFFFFFFF`: 32 位设备(最多 4GB)
60    ///   - `0xFFFFFFFFFFFFFFFF`: 64 位设备(全地址空间)
61    /// - `osal`: 实现 `DmaOp` trait 的平台抽象层引用
62    ///
63    /// # 示例
64    ///
65    /// ```rust,ignore
66    /// use dma_api::DeviceDma;
67    ///
68    /// let device = DeviceDma::new(0xFFFFFFFF, &my_dma_impl);
69    /// ```
70    pub fn new(dma_mask: u64, osal: &'static dyn DmaOp) -> Self {
71        Self {
72            mask: dma_mask,
73            os: osal,
74        }
75    }
76
77    /// 获取设备的 DMA 地址掩码。
78    ///
79    /// # 返回
80    ///
81    /// 返回设备的 DMA 掩码值,表示设备可寻址的最大地址范围。
82    pub fn dma_mask(&self) -> u64 {
83        self.mask
84    }
85
86    /// 刷新 CPU 缓存到内存(clean 操作)。
87    ///
88    /// 将指定地址范围的 CPU 缓存数据写回到内存,确保设备可以读取到最新数据。
89    /// 用于 `ToDevice` 和 `Bidirectional` 方向的 DMA 传输前。
90    ///
91    /// # 参数
92    ///
93    /// - `addr`: 内存起始地址
94    /// - `size`: 内存大小(字节)
95    pub fn flush(&self, addr: NonNull<u8>, size: usize) {
96        self.os.flush(addr, size)
97    }
98
99    /// 使 CPU 缓存失效(invalidate 操作)。
100    ///
101    /// 使指定地址范围的 CPU 缓存失效,强制 CPU 从内存重新读取数据。
102    /// 用于 `FromDevice` 和 `Bidirectional` 方向的 DMA 传输后。
103    ///
104    /// # 参数
105    ///
106    /// - `addr`: 内存起始地址
107    /// - `size`: 内存大小(字节)
108    pub fn invalidate(&self, addr: NonNull<u8>, size: usize) {
109        self.os.invalidate(addr, size)
110    }
111
112    /// 刷新并使 CPU 缓存失效(clean and invalidate 操作)。
113    ///
114    /// 同时执行刷新和失效操作,用于确保缓存和内存完全同步。
115    ///
116    /// # 参数
117    ///
118    /// - `addr`: 内存起始地址
119    /// - `size`: 内存大小(字节)
120    pub fn flush_invalidate(&self, addr: NonNull<u8>, size: usize) {
121        self.os.flush_invalidate(addr, size)
122    }
123
124    /// 获取系统页大小。
125    ///
126    /// # 返回
127    ///
128    /// 返回系统的页大小(字节),通常为 4096。
129    pub fn page_size(&self) -> usize {
130        self.os.page_size()
131    }
132
133    fn prepare_read(
134        &self,
135        handle: &DmaMapHandle,
136        offset: usize,
137        size: usize,
138        direction: DmaDirection,
139    ) {
140        self.os.prepare_read(handle, offset, size, direction)
141    }
142
143    fn confirm_write(
144        &self,
145        handle: &DmaMapHandle,
146        offset: usize,
147        size: usize,
148        direction: DmaDirection,
149    ) {
150        self.os.confirm_write(handle, offset, size, direction)
151    }
152
153    unsafe fn alloc_coherent(&self, layout: core::alloc::Layout) -> Result<DmaHandle, DmaError> {
154        let res = unsafe { self.os.alloc_coherent(self.mask, layout) }.ok_or(DmaError::NoMemory)?;
155        match self.check_handle(&res) {
156            Ok(()) => Ok(res),
157            Err(e) => {
158                unsafe { self.dealloc_coherent(res) };
159                Err(e)
160            }
161        }
162    }
163
164    unsafe fn dealloc_coherent(&self, handle: DmaHandle) {
165        unsafe { self.os.dealloc_coherent(handle) }
166    }
167
168    fn check_handle(&self, handle: &DmaHandle) -> Result<(), DmaError> {
169        let addr: u64 = handle.dma_addr.into();
170
171        let in_mask = if handle.size() == 0 {
172            addr <= self.dma_mask()
173        } else {
174            addr.checked_add(handle.size().saturating_sub(1) as u64)
175                .map(|end| end <= self.dma_mask())
176                .unwrap_or(false)
177        };
178
179        if !in_mask {
180            return Err(DmaError::DmaMaskNotMatch {
181                addr: handle.dma_addr,
182                mask: self.dma_mask(),
183            });
184        }
185
186        let is_aligned = handle
187            .dma_addr
188            .as_u64()
189            .is_multiple_of(handle.align() as u64);
190        if !is_aligned {
191            return Err(DmaError::AlignMismatch {
192                address: handle.dma_addr,
193                required: handle.align(),
194            });
195        }
196
197        Ok(())
198    }
199
200    unsafe fn _map_single(
201        &self,
202        addr: NonNull<u8>,
203        size: NonZeroUsize,
204        align: usize,
205        direction: DmaDirection,
206    ) -> Result<DmaMapHandle, DmaError> {
207        let res = unsafe { self.os.map_single(self.mask, addr, size, align, direction) }?;
208        match self.check_handle(&res) {
209            Ok(()) => Ok(res),
210            Err(e) => {
211                unsafe { self.unmap_single(res) };
212                Err(e)
213            }
214        }
215    }
216
217    unsafe fn unmap_single(&self, handle: DmaMapHandle) {
218        unsafe { self.os.unmap_single(handle) }
219    }
220
221    /// 创建默认对齐的 DMA 数组。
222    ///
223    /// 分配一个指定大小的 DMA 可访问数组,内存初始化为零。
224    /// 数组的对齐方式使用类型 `T` 的默认对齐值。
225    ///
226    /// # 类型参数
227    ///
228    /// - `T`: 数组元素类型,必须是 `Sized` 并且实现了 `Default`
229    ///
230    /// # 参数
231    ///
232    /// - `size`: 数组长度(元素个数)
233    /// - `direction`: DMA 传输方向,决定缓存同步策略
234    ///
235    /// # 返回
236    ///
237    /// 成功时返回 `DArray<T>` 容器,失败时返回 `DmaError`
238    ///
239    /// # 示例
240    ///
241    /// ```rust,ignore
242    /// let dma_array = device.array_zero::<u32>(100, DmaDirection::FromDevice)?;
243    /// ```
244    pub fn array_zero<T>(
245        &self,
246        size: usize,
247        direction: DmaDirection,
248    ) -> Result<array::DArray<T>, DmaError> {
249        array::DArray::new_zero(self, size, direction)
250    }
251
252    /// 创建指定对齐的 DMA 数组。
253    ///
254    /// 分配一个指定大小和对齐要求的 DMA 可访问数组,内存初始化为零。
255    ///
256    /// # 类型参数
257    ///
258    /// - `T`: 数组元素类型,必须是 `Sized` 并且实现了 `Default`
259    ///
260    /// # 参数
261    ///
262    /// - `size`: 数组长度(元素个数)
263    /// - `align`: 对齐字节数(至少等于 `core::mem::align_of::<T>()`)
264    /// - `direction`: DMA 传输方向,决定缓存同步策略
265    ///
266    /// # 返回
267    ///
268    /// 成功时返回 `DArray<T>` 容器,失败时返回 `DmaError`
269    ///
270    /// # 示例
271    ///
272    /// ```rust,ignore
273    /// // 创建 64 字节对齐的数组
274    /// let dma_array = device
275    ///     .array_zero_with_align::<u32>(100, 64, DmaDirection::FromDevice)?;
276    /// ```
277    pub fn array_zero_with_align<T>(
278        &self,
279        size: usize,
280        align: usize,
281        direction: DmaDirection,
282    ) -> Result<array::DArray<T>, DmaError> {
283        array::DArray::new_zero_with_align(self, size, align, direction)
284    }
285
286    /// 创建默认对齐的 DMA Box。
287    ///
288    /// 分配一个 DMA 可访问的单值容器,内存初始化为零。
289    /// 适合存储 DMA 描述符、配置结构等单个对象。
290    ///
291    /// # 类型参数
292    ///
293    /// - `T`: 存储的值类型,必须是 `Sized` 并且实现了 `Default`
294    ///
295    /// # 参数
296    ///
297    /// - `direction`: DMA 传输方向,决定缓存同步策略
298    ///
299    /// # 返回
300    ///
301    /// 成功时返回 `DBox<T>` 容器,失败时返回 `DmaError`
302    ///
303    /// # 示例
304    ///
305    /// ```rust,ignore
306    /// #[derive(Default)]
307    /// struct Descriptor {
308    ///     addr: u64,
309    ///     length: u32,
310    /// }
311    ///
312    /// let dma_desc = device.box_zero::<Descriptor>(DmaDirection::ToDevice)?;
313    /// ```
314    pub fn box_zero<T>(&self, direction: DmaDirection) -> Result<dbox::DBox<T>, DmaError> {
315        dbox::DBox::new_zero(self, direction)
316    }
317
318    /// 创建指定对齐的 DMA Box。
319    ///
320    /// 分配一个指定对齐要求的 DMA 可访问单值容器,内存初始化为零。
321    ///
322    /// # 类型参数
323    ///
324    /// - `T`: 存储的值类型,必须是 `Sized` 并且实现了 `Default`
325    ///
326    /// # 参数
327    ///
328    /// - `align`: 对齐字节数(至少等于 `core::mem::align_of::<T>()`)
329    /// - `direction`: DMA 传输方向,决定缓存同步策略
330    ///
331    /// # 返回
332    ///
333    /// 成功时返回 `DBox<T>` 容器,失败时返回 `DmaError`
334    ///
335    /// # 示例
336    ///
337    /// ```rust,ignore
338    /// let dma_desc = device
339    ///     .box_zero_with_align::<Descriptor>(64, DmaDirection::ToDevice)?;
340    /// ```
341    pub fn box_zero_with_align<T>(
342        &self,
343        align: usize,
344        direction: DmaDirection,
345    ) -> Result<dbox::DBox<T>, DmaError> {
346        dbox::DBox::new_zero_with_align(self, align, direction)
347    }
348
349    /// 映射现有缓冲区为 DMA 可访问。
350    ///
351    /// 将已存在的缓冲区(如栈数组或堆分配的 slice)映射为 DMA 可访问区域。
352    /// 返回的 `SArrayPtr` 在离开作用域时自动解除映射。
353    ///
354    /// # 缓存同步
355    ///
356    /// **重要**: 此方法创建的映射**不会**自动同步缓存。
357    /// 你必须手动调用 `SArrayPtr` 的方法进行缓存同步:
358    /// - `to_vec()`: 读取前自动失效整个范围
359    /// - `copy_from_slice()`: 写入后自动刷新整个范围
360    ///
361    /// # 类型参数
362    ///
363    /// - `T`: 数组元素类型
364    ///
365    /// # 参数
366    ///
367    /// - `buff`: 要映射的缓冲区切片
368    /// - `align`: 对齐字节数
369    /// - `direction`: DMA 传输方向,决定手动缓存同步的行为
370    ///
371    /// # 返回
372    ///
373    /// 成功时返回 `SArrayPtr<T>` 映射句柄,失败时返回 `DmaError`
374    ///
375    /// # 示例
376    ///
377    /// ```rust,ignore
378    /// let mut buffer = [0u8; 4096];
379    ///
380    /// // 映射用于 DMA 写入
381    /// let mapping = device.map_single_array(&buffer, 64, DmaDirection::ToDevice)?;
382    ///
383    /// // 必须手动刷新缓存
384    /// mapping.copy_from_slice(&data);
385    ///
386    /// // ... 启动 DMA 传输 ...
387    ///
388    /// // 映射在作用域结束时自动解映射
389    /// ```
390    pub fn map_single_array<T>(
391        &self,
392        buff: &[T],
393        align: usize,
394        direction: DmaDirection,
395    ) -> Result<SArrayPtr<T>, DmaError> {
396        SArrayPtr::map_single(self, buff, align, direction)
397    }
398}