use core::{alloc::Layout, cmp::PartialOrd, ptr::NonNull};
use derive_more::{
Add, AddAssign, Debug, Display, Div, From, Into, Mul, MulAssign, Sub, SubAssign,
};
#[derive(
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Hash,
From,
Into,
Add,
AddAssign,
Mul,
MulAssign,
Sub,
SubAssign,
Div,
)]
#[debug("{}", format_args!("{_0:#X}"))]
#[display("{}", format_args!("{_0:#X}"))]
pub struct DmaAddr(u64);
impl DmaAddr {
pub fn as_u64(&self) -> u64 {
self.0
}
pub fn checked_add(&self, rhs: u64) -> Option<Self> {
self.0.checked_add(rhs).map(DmaAddr)
}
}
impl PartialEq<u64> for DmaAddr {
fn eq(&self, other: &u64) -> bool {
self.0 == *other
}
}
impl PartialOrd<u64> for DmaAddr {
fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DmaConstraints {
pub addr_mask: u64,
pub align: usize,
pub boundary: Option<usize>,
pub max_segment_size: Option<usize>,
}
impl DmaConstraints {
pub const fn new(addr_mask: u64) -> Self {
Self {
addr_mask,
align: 1,
boundary: None,
max_segment_size: None,
}
}
pub fn with_align(mut self, align: usize) -> Self {
self.align = align.max(1);
self
}
pub fn with_boundary(mut self, boundary: usize) -> Self {
self.boundary = Some(boundary.max(1));
self
}
pub fn with_max_segment_size(mut self, max_segment_size: usize) -> Self {
self.max_segment_size = Some(max_segment_size);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DmaDirection {
ToDevice,
FromDevice,
Bidirectional,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum DmaError {
#[error("DMA allocation failed")]
NoMemory,
#[error("Invalid layout")]
LayoutError(#[from] core::alloc::LayoutError),
#[error("DMA address {addr} does not match device mask {mask:#X}")]
DmaMaskNotMatch { addr: DmaAddr, mask: u64 },
#[error("DMA align mismatch: required={required:#X}, but address={address}")]
AlignMismatch { required: usize, address: DmaAddr },
#[error("DMA segment size {size:#X} exceeds max segment size {max:#X}")]
SegmentTooLarge { size: usize, max: usize },
#[error("DMA address range crosses boundary {boundary:#X}: addr={addr}, size={size:#X}")]
BoundaryCross {
addr: DmaAddr,
size: usize,
boundary: usize,
},
#[error("Null pointer provided for DMA mapping")]
NullPointer,
#[error("Zero-sized buffer cannot be used for DMA")]
ZeroSizedBuffer,
}
pub unsafe trait DmaPod: Copy {}
unsafe impl<T: Copy> DmaPod for T {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DmaAllocHandle {
pub(crate) cpu_addr: NonNull<u8>,
pub(crate) dma_addr: DmaAddr,
pub(crate) layout: Layout,
}
impl DmaAllocHandle {
pub unsafe fn new(cpu_addr: NonNull<u8>, dma_addr: DmaAddr, layout: Layout) -> Self {
Self {
cpu_addr,
dma_addr,
layout,
}
}
pub fn size(&self) -> usize {
self.layout.size()
}
pub fn align(&self) -> usize {
self.layout.align()
}
pub fn as_ptr(&self) -> NonNull<u8> {
self.cpu_addr
}
pub fn dma_addr(&self) -> DmaAddr {
self.dma_addr
}
pub fn layout(&self) -> Layout {
self.layout
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DmaMapHandle {
pub(crate) cpu_addr: NonNull<u8>,
pub(crate) dma_addr: DmaAddr,
pub(crate) layout: Layout,
pub(crate) bounce_ptr: Option<NonNull<u8>>,
}
impl DmaMapHandle {
pub unsafe fn new(
cpu_addr: NonNull<u8>,
dma_addr: DmaAddr,
layout: Layout,
bounce_ptr: Option<NonNull<u8>>,
) -> Self {
Self {
cpu_addr,
dma_addr,
layout,
bounce_ptr,
}
}
pub fn size(&self) -> usize {
self.layout.size()
}
pub fn align(&self) -> usize {
self.layout.align()
}
pub fn as_ptr(&self) -> NonNull<u8> {
self.cpu_addr
}
pub fn dma_addr(&self) -> DmaAddr {
self.dma_addr
}
pub fn layout(&self) -> Layout {
self.layout
}
pub fn bounce_ptr(&self) -> Option<NonNull<u8>> {
self.bounce_ptr
}
}