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}