#[cfg(test)]
pub mod fake;
use crate::{Error, PAGE_SIZE, Result};
use core::{marker::PhantomData, ptr::NonNull};
pub type PhysAddr = u64;
#[derive(Debug)]
pub struct Dma<H: Hal> {
paddr: PhysAddr,
vaddr: NonNull<u8>,
pages: usize,
_hal: PhantomData<H>,
}
unsafe impl<H: Hal> Send for Dma<H> {}
unsafe impl<H: Hal> Sync for Dma<H> {}
impl<H: Hal> Dma<H> {
pub fn new(pages: usize, direction: BufferDirection) -> Result<Self> {
let (paddr, vaddr) = H::dma_alloc(pages, direction);
if paddr == 0 {
return Err(Error::DmaError);
}
Ok(Self {
paddr,
vaddr,
pages,
_hal: PhantomData,
})
}
pub fn paddr(&self) -> PhysAddr {
self.paddr
}
pub fn vaddr(&self, offset: usize) -> NonNull<u8> {
assert!(offset < self.pages * PAGE_SIZE);
NonNull::new((self.vaddr.as_ptr() as usize + offset) as _).unwrap()
}
pub fn raw_slice(&self) -> NonNull<[u8]> {
let raw_slice =
core::ptr::slice_from_raw_parts_mut(self.vaddr(0).as_ptr(), self.pages * PAGE_SIZE);
NonNull::new(raw_slice).unwrap()
}
}
impl<H: Hal> Drop for Dma<H> {
fn drop(&mut self) {
let err = unsafe { H::dma_dealloc(self.paddr, self.vaddr, self.pages) };
assert_eq!(err, 0, "failed to deallocate DMA");
}
}
pub unsafe trait Hal {
fn dma_alloc(pages: usize, direction: BufferDirection) -> (PhysAddr, NonNull<u8>);
unsafe fn dma_dealloc(paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32;
unsafe fn mmio_phys_to_virt(paddr: PhysAddr, size: usize) -> NonNull<u8>;
unsafe fn share(buffer: NonNull<[u8]>, direction: BufferDirection) -> PhysAddr;
unsafe fn unshare(paddr: PhysAddr, buffer: NonNull<[u8]>, direction: BufferDirection);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BufferDirection {
DriverToDevice,
DeviceToDriver,
Both,
}