use alloc::{borrow::Cow, sync::Arc};
use core::{alloc::Layout, any::Any, ffi::c_int};
use ax_dma::DMAInfo;
use ax_errno::{AxError, AxResult};
use ax_memory_addr::{PAGE_SIZE_4K, PhysAddr, PhysAddrRange};
use axpoll::{IoEvents, Pollable};
use linux_raw_sys::general::O_RDWR;
use super::{FileLike, Kstat};
use crate::pseudofs::DeviceMmap;
struct DmaBufAlloc {
dma: DMAInfo,
size: usize,
align: usize,
}
unsafe impl Send for DmaBufAlloc {}
unsafe impl Sync for DmaBufAlloc {}
impl Drop for DmaBufAlloc {
fn drop(&mut self) {
if let Ok(layout) = Layout::from_size_align(self.size, self.align) {
unsafe { ax_dma::dealloc_coherent_pages(self.dma, layout) };
}
}
}
pub struct DmaBufFile {
alloc: Arc<DmaBufAlloc>,
}
impl DmaBufFile {
pub fn alloc(len: usize) -> AxResult<Self> {
let align = PAGE_SIZE_4K;
let size = len
.checked_next_multiple_of(align)
.ok_or(AxError::InvalidInput)?
.max(align);
let layout = Layout::from_size_align(size, align).map_err(|_| AxError::InvalidInput)?;
let dma =
unsafe { ax_dma::alloc_coherent_pages_dma32(layout) }.map_err(|_| AxError::NoMemory)?;
Ok(Self {
alloc: Arc::new(DmaBufAlloc { dma, size, align }),
})
}
pub fn phys_range(&self) -> PhysAddrRange {
PhysAddrRange::from_start_size(
PhysAddr::from(self.alloc.dma.bus_addr.as_u64() as usize),
self.alloc.size,
)
}
pub fn phys_base(&self) -> usize {
self.alloc.dma.bus_addr.as_u64() as usize
}
#[cfg(feature = "rknpu")]
pub fn size(&self) -> usize {
self.alloc.size
}
}
#[cfg(feature = "rknpu")]
pub trait ContiguousDmaBuf {
fn dma_phys_base(&self) -> usize;
fn dma_size(&self) -> usize;
fn dma_cpu_base(&self) -> Option<usize>;
fn dma_retainer(&self) -> Arc<dyn Any + Send + Sync>;
}
#[cfg(feature = "rknpu")]
impl ContiguousDmaBuf for DmaBufFile {
fn dma_phys_base(&self) -> usize {
self.phys_base()
}
fn dma_size(&self) -> usize {
self.size()
}
fn dma_cpu_base(&self) -> Option<usize> {
Some(self.alloc.dma.cpu_addr.as_ptr() as usize)
}
fn dma_retainer(&self) -> Arc<dyn Any + Send + Sync> {
self.alloc.clone()
}
}
pub fn resolve_contiguous_dmabuf(fd: c_int) -> Option<Arc<DmaBufFile>> {
let file = super::get_file_like(fd).ok()?;
file.downcast_arc::<DmaBufFile>().ok()
}
impl Pollable for DmaBufFile {
fn poll(&self) -> IoEvents {
IoEvents::IN | IoEvents::OUT
}
fn register(&self, _context: &mut core::task::Context<'_>, _events: IoEvents) {}
}
impl FileLike for DmaBufFile {
fn stat(&self) -> AxResult<Kstat> {
Ok(Kstat {
size: self.alloc.size as u64,
..Default::default()
})
}
fn path(&self) -> Cow<'_, str> {
Cow::Borrowed("/dev/dma_heap_buffer")
}
fn open_flags(&self) -> u32 {
O_RDWR
}
fn device_mmap(&self, _offset: u64, _length: u64) -> AxResult<DeviceMmap> {
let retainer: Arc<dyn Any + Send + Sync> = self.alloc.clone();
Ok(DeviceMmap::Physical(self.phys_range(), Some(retainer)))
}
}