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