Skip to main content

dma_api/
def.rs

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