use alloc::sync::Arc;
use core::any::Any;
use axfs_ng_vfs::{DeviceId, VfsError, VfsResult};
use bytemuck::{AnyBitPattern, NoUninit};
use linux_raw_sys::general::O_CLOEXEC;
use crate::{
file::{add_file_like, close_file_like, dmabuf::DmaBufFile},
mm::{UserConstPtr, UserPtr},
pseudofs::DeviceOps,
};
pub const DMA_HEAP_DEVICE_ID: DeviceId = DeviceId::new(0xF1, 0x20);
pub const HEAP_NAMES: &[&str] = &[
"system",
"system-uncached",
"system-dma32",
"system-uncached-dma32",
"cma",
"cma-uncached",
"cma-dma32",
"cma-uncached-dma32",
];
const DMA_HEAP_IOCTL_ALLOC: u32 = 0xC018_4800;
#[repr(C)]
#[derive(Clone, Copy, Default, AnyBitPattern, NoUninit)]
struct DmaHeapAllocData {
len: u64,
fd: u32,
fd_flags: u32,
heap_flags: u64,
}
pub struct DmaHeap;
impl DeviceOps for DmaHeap {
fn read_at(&self, _buf: &mut [u8], _offset: u64) -> VfsResult<usize> {
Ok(0)
}
fn write_at(&self, buf: &[u8], _offset: u64) -> VfsResult<usize> {
Ok(buf.len())
}
fn as_any(&self) -> &dyn Any {
self
}
fn ioctl(&self, current: &crate::task::UserTaskRef, cmd: u32, arg: usize) -> VfsResult<usize> {
if cmd != DMA_HEAP_IOCTL_ALLOC {
return Err(VfsError::NotATty);
}
if arg == 0 {
return Err(VfsError::InvalidInput);
}
let mut data = copy_in(current, arg)?;
if data.len == 0 {
return Err(VfsError::InvalidInput);
}
let buf = DmaBufFile::alloc(data.len as usize)?;
let cloexec = data.fd_flags & O_CLOEXEC != 0;
let fd = add_file_like(Arc::new(buf), cloexec)?;
data.fd = fd as u32;
if let Err(e) = copy_out(current, &data, arg) {
let _ = close_file_like(fd);
return Err(e);
}
Ok(0)
}
}
fn copy_in(current: &crate::task::UserTaskRef, uaddr: usize) -> VfsResult<DmaHeapAllocData> {
UserConstPtr::<DmaHeapAllocData>::from(uaddr)
.read(current)
.map_err(|_| VfsError::InvalidData)
}
fn copy_out(
current: &crate::task::UserTaskRef,
src: &DmaHeapAllocData,
uaddr: usize,
) -> VfsResult<()> {
UserPtr::<DmaHeapAllocData>::from(uaddr)
.write(current, *src)
.map_err(|_| VfsError::InvalidData)
}