Skip to main content

dma_api/
def.rs

1use core::{alloc::Layout, cmp::PartialOrd, ops::Deref, ptr::NonNull};
2
3use derive_more::{
4    Add, AddAssign, Debug, Display, Div, From, Into, Mul, MulAssign, Sub, SubAssign,
5};
6
7#[derive(
8    Debug,
9    Display,
10    Clone,
11    Copy,
12    PartialEq,
13    Eq,
14    PartialOrd,
15    Hash,
16    From,
17    Into,
18    Add,
19    AddAssign,
20    Mul,
21    MulAssign,
22    Sub,
23    SubAssign,
24    Div,
25)]
26#[debug("{}", format_args!("{_0:#X}"))]
27#[display("{}", format_args!("{_0:#X}"))]
28pub struct DmaAddr(u64);
29
30impl DmaAddr {
31    pub fn as_u64(&self) -> u64 {
32        self.0
33    }
34
35    pub fn checked_add(&self, rhs: u64) -> Option<Self> {
36        self.0.checked_add(rhs).map(DmaAddr)
37    }
38}
39
40impl PartialEq<u64> for DmaAddr {
41    fn eq(&self, other: &u64) -> bool {
42        self.0 == *other
43    }
44}
45
46impl PartialOrd<u64> for DmaAddr {
47    fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
48        self.0.partial_cmp(other)
49    }
50}
51
52/// 物理地址类型
53#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, From, Into, Add, Mul, Sub)]
54#[debug("{}", format_args!("{_0:#X}"))]
55#[display("{}", format_args!("{_0:#X}"))]
56pub struct PhysAddr(u64);
57
58impl PhysAddr {
59    pub fn as_u64(&self) -> u64 {
60        self.0
61    }
62}
63
64/// DMA 传输方向
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66pub enum DmaDirection {
67    /// 数据从 CPU 传输到设备 (DMA_TO_DEVICE)
68    ToDevice,
69    /// 数据从设备传输到 CPU (DMA_FROM_DEVICE)
70    FromDevice,
71    /// 双向传输 (DMA_BIDIRECTIONAL)
72    Bidirectional,
73}
74
75/// DMA 错误类型
76#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
77pub enum DmaError {
78    #[error("DMA allocation failed")]
79    NoMemory,
80    #[error("Invalid layout")]
81    LayoutError(#[from] core::alloc::LayoutError),
82    #[error("DMA address {addr} does not match device mask {mask:#X}")]
83    DmaMaskNotMatch { addr: DmaAddr, mask: u64 },
84    #[error("DMA align mismatch: required={required:#X}, but address={address}")]
85    AlignMismatch { required: usize, address: DmaAddr },
86    #[error("Null pointer provided for DMA mapping")]
87    NullPointer,
88    #[error("Zero-sized buffer cannot be used for DMA")]
89    ZeroSizedBuffer,
90}
91
92/// Handle for DMA memory allocation.
93///
94/// Manages DMA memory buffers that may require special alignment or DMA address mask
95/// constraints. When the original virtual address doesn't meet alignment or mask
96/// requirements, an additional aligned buffer is allocated and stored in `alloc_virt`.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub struct DmaHandle {
99    /// Original virtual address provided by the user
100    pub(crate) cpu_addr: NonNull<u8>,
101    /// DMA address visible to devices
102    pub(crate) dma_addr: DmaAddr,
103    /// Memory layout specification (size and alignment)
104    pub(crate) layout: Layout,
105    // /// Additional allocated virtual address if the original doesn't satisfy
106    // /// alignment or DMA mask requirements when mapping for DMA.
107    // pub(crate) map_alloc_virt: Option<NonNull<u8>>,
108}
109
110impl DmaHandle {
111    /// 为 `alloc_coherent` 操作创建 `DmaHandle`。
112    ///
113    /// 此构造函数专门用于 DMA 一致性内存分配场景,其中:
114    /// - 内存是专门为 DMA 分配的(零初始化)
115    /// - CPU 和设备看到同一个虚拟地址
116    /// - 不需要额外的对齐缓冲区
117    ///
118    /// # 特性保证
119    ///
120    /// - 内存已被零初始化
121    ///
122    /// # Safety
123    ///
124    /// 调用者必须确保:
125    /// - `origin_virt` 指向有效内存,生命周期与 handle 相同
126    /// - `dma_addr` 是与 `origin_virt` 对应的设备可访问地址
127    /// - `layout` 正确描述内存的大小和对齐
128    /// - 内存必须保持有效直到被正确释放
129    pub unsafe fn new(cpu_addr: NonNull<u8>, dma_addr: DmaAddr, layout: Layout) -> Self {
130        Self {
131            cpu_addr,
132            dma_addr,
133            layout,
134        }
135    }
136
137    /// Returns the size of the DMA buffer in bytes.
138    pub fn size(&self) -> usize {
139        self.layout.size()
140    }
141
142    /// Returns the alignment requirement of the DMA buffer in bytes.
143    pub fn align(&self) -> usize {
144        self.layout.align()
145    }
146
147    /// Returns the virtual address to access data.
148    pub fn as_ptr(&self) -> NonNull<u8> {
149        self.cpu_addr
150    }
151
152    /// Returns the DMA address visible to devices.
153    pub fn dma_addr(&self) -> DmaAddr {
154        self.dma_addr
155    }
156
157    /// Returns the memory layout used for this DMA allocation.
158    pub fn layout(&self) -> Layout {
159        self.layout
160    }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
164pub struct DmaMapHandle {
165    pub(crate) handle: DmaHandle,
166    pub(crate) map_alloc_virt: Option<NonNull<u8>>,
167}
168
169impl Deref for DmaMapHandle {
170    type Target = DmaHandle;
171    fn deref(&self) -> &Self::Target {
172        &self.handle
173    }
174}
175
176impl DmaMapHandle {
177    /// 为 `map_single` 操作创建 `DmaMapHandle`。
178    ///
179    /// 此构造函数用于将现有缓冲区映射为 DMA 可访问的场景,其中:
180    /// - 缓冲区可能已经存在于用户空间
181    /// - 如果原地址不满足对齐或掩码要求,会分配额外的对齐缓冲区
182    /// - `alloc_virt` 存储额外的对齐缓冲区地址(如果分配了)
183    ///
184    /// # 特性保证
185    ///
186    /// - 如果原地址满足要求,`alloc_virt` 为 `None`
187    /// - 如果分配了对齐缓冲区,`alloc_virt` 包含其地址
188    ///
189    /// # Safety
190    ///
191    /// 调用者必须确保:
192    /// - `cpu_addr` 指向有效内存,生命周期与 handle 相同
193    /// - `dma_addr` 是与 `cpu_addr` 对应的设备可访问地址
194    /// - `layout` 正确描述内存的大小和对齐
195    /// - `alloc_virt`(如果提供)必须指向有效分配的内存
196    /// - 内存必须保持有效直到 `unmap_single` 被调用
197    /// - 必须与 `DmaOp::unmap_single` 配对使用以防止内存泄漏
198    pub unsafe fn new(
199        cpu_addr: NonNull<u8>,
200        dma_addr: DmaAddr,
201        layout: Layout,
202        alloc_virt: Option<NonNull<u8>>,
203    ) -> Self {
204        let handle = DmaHandle {
205            cpu_addr,
206            dma_addr,
207            layout,
208        };
209        Self {
210            handle,
211            map_alloc_virt: alloc_virt,
212        }
213    }
214
215    pub fn alloc_virt(&self) -> Option<NonNull<u8>> {
216        self.map_alloc_virt
217    }
218}