Skip to main content

dma_api/
dbox.rs

1use core::ptr::NonNull;
2
3use crate::{DeviceDma, DmaAddr, DmaDirection, DmaError, common::DCommon};
4
5/// DMA 可访问的单值容器。
6///
7/// `DBox<T>` 提供单个值的 DMA 可访问存储,支持自动缓存同步。
8/// 每次访问值(`read`/`write`/`modify`)时都会根据 DMA 方向自动处理缓存操作。
9///
10/// # 类型参数
11///
12/// - `T`: 存储的值类型
13///
14/// # 缓存同步
15///
16/// 缓存同步在每次访问时自动执行:
17/// - `read()`: 读取前使 CPU 缓存失效(FromDevice/Bidirectional)
18/// - `write(value)`: 写入后刷新 CPU 缓存(ToDevice/Bidirectional)
19/// - `modify(f)`: 先失效缓存,执行闭包,再刷新缓存
20///
21/// # 示例
22///
23/// ```rust,ignore
24/// use dma_api::{DeviceDma, DmaDirection};
25///
26/// #[derive(Default)]
27/// struct Descriptor {
28///     addr: u64,
29///     length: u32,
30/// }
31///
32/// let device = DeviceDma::new(0xFFFFFFFF, &my_dma_impl);
33///
34/// // 分配描述符
35/// let mut dma_desc = device
36///     .box_zero_with_align::<Descriptor>(64, DmaDirection::ToDevice)
37///     .expect("Failed to allocate");
38///
39/// // 配置描述符(自动刷新缓存)
40/// dma_desc.modify(|d| d.length = 4096);
41///
42/// // 获取 DMA 地址给硬件
43/// let desc_addr = dma_desc.dma_addr();
44/// ```
45///
46/// # 生命周期
47///
48/// `DBox` 拥有其分配的 DMA 内存,在离开作用域时自动释放。
49pub struct DBox<T> {
50    data: DCommon,
51    _marker: core::marker::PhantomData<T>,
52}
53
54unsafe impl<T> Send for DBox<T> where T: Send {}
55
56impl<T> DBox<T> {
57    pub(crate) fn new_zero(os: &DeviceDma, direction: DmaDirection) -> Result<Self, DmaError> {
58        let layout = core::alloc::Layout::from_size_align(
59            core::mem::size_of::<T>(),
60            core::mem::align_of::<T>(),
61        )?;
62        let data = DCommon::new_zero(os, layout, direction)?;
63        Ok(Self {
64            data,
65            _marker: core::marker::PhantomData,
66        })
67    }
68
69    pub(crate) fn new_zero_with_align(
70        os: &DeviceDma,
71        align: usize,
72        direction: DmaDirection,
73    ) -> Result<Self, DmaError> {
74        let layout = core::alloc::Layout::from_size_align(
75            core::mem::size_of::<T>(),
76            align.max(core::mem::align_of::<T>()),
77        )?;
78        let data = DCommon::new_zero(os, layout, direction)?;
79        Ok(Self {
80            data,
81            _marker: core::marker::PhantomData,
82        })
83    }
84
85    /// 获取 DMA 地址。
86    ///
87    /// 返回设备用于访问此 DMA 缓冲区的物理/DMA 地址。
88    /// 将此地址传递给硬件设备以配置 DMA 操作。
89    ///
90    /// # 返回
91    ///
92    /// DMA 地址
93    pub fn dma_addr(&self) -> DmaAddr {
94        self.data.handle.dma_addr
95    }
96
97    /// 读取存储的值。
98    ///
99    /// 根据 DMA 方向自动处理缓存同步:
100    /// - `FromDevice`/`Bidirectional`: 读取前使 CPU 缓存失效
101    /// - `ToDevice`: 无缓存操作
102    ///
103    /// # 返回
104    ///
105    /// 存储的值
106    pub fn read(&self) -> T {
107        unsafe {
108            self.data.prepare_read(0, core::mem::size_of::<T>());
109            let ptr = self.data.handle.cpu_addr.cast::<T>();
110            ptr.read()
111        }
112    }
113
114    /// 写入新值。
115    ///
116    /// 根据 DMA 方向自动处理缓存同步:
117    /// - `ToDevice`/`Bidirectional`: 写入后刷新 CPU 缓存
118    /// - `FromDevice`: 无缓存操作
119    ///
120    /// # 参数
121    ///
122    /// - `value`: 要写入的值
123    pub fn write(&mut self, value: T) {
124        unsafe {
125            let ptr = self.data.handle.cpu_addr.cast::<T>();
126            ptr.write(value);
127            self.data.confirm_write(0, core::mem::size_of::<T>());
128        }
129    }
130
131    /// 修改值(read-modify-write 模式)。
132    ///
133    /// 此方法等价于先调用 `read()`,然后对值执行闭包,最后调用 `write()`。
134    /// 缓存同步操作:读取前失效缓存,写入后刷新缓存。
135    ///
136    /// # 参数
137    ///
138    /// - `f`: 修改值的闭包
139    ///
140    /// # 示例
141    ///
142    /// ```rust,ignore
143    /// dma_box.modify(|v| v.field += 10);
144    /// ```
145    pub fn modify(&mut self, f: impl FnOnce(&mut T)) {
146        let mut value = self.read();
147        f(&mut value);
148        self.write(value);
149    }
150
151    /// 获取指向存储值的指针。
152    ///
153    /// # 返回
154    ///
155    /// 指向存储值的非空指针
156    pub fn as_ptr(&self) -> NonNull<T> {
157        self.data.handle.as_ptr().cast::<T>()
158    }
159
160    /// 获取底层缓冲区的可变切片。
161    ///
162    /// # Safety
163    ///
164    /// - 调用者必须确保在使用该切片期间,设备不会访问此内存区域
165    /// - 调用者必须手动处理缓存同步(flush/invalidate)
166    pub unsafe fn as_buff_mut(&mut self) -> &mut [u8] {
167        self.data.as_mut_slice()
168    }
169}