use core::{
any::Any,
convert::TryFrom,
ffi::{c_char, c_ulong},
};
use ax_runtime::hal::cpu::asm::user_copy;
use axfs_ng_vfs::{DeviceId, NodeFlags, VfsError, VfsResult};
use super::rknpu_drm::DrmVersion;
use crate::pseudofs::{
DeviceOps,
dev::rknpu_drm::{io_size, ioctl_nr, is_driver_ioctl},
};
const DRM0_NAME: &str = "rockchip";
const DRM0_DATE: &str = "20140818";
const DRM0_DESC: &str = "RockChip Soc DRM";
pub const RKNPU_DEVICE_ID: DeviceId = DeviceId::new(251, 0);
pub const CARD0_SYSTEM_DEVICE_ID: DeviceId = DeviceId::new(0xe2, 0);
pub struct Card0;
impl Card0 {
pub fn new() -> Card0 {
Self
}
}
impl Default for Card0 {
fn default() -> Self {
Self::new()
}
}
impl DeviceOps for Card0 {
fn read_at(&self, _buf: &mut [u8], _offset: u64) -> VfsResult<usize> {
trace!("card0: read_at called");
Err(VfsError::InvalidInput)
}
fn write_at(&self, _buf: &[u8], _offset: u64) -> VfsResult<usize> {
trace!("card0: write_at called");
Err(VfsError::InvalidInput)
}
fn ioctl(&self, cmd: u32, arg: usize) -> VfsResult<usize> {
if arg == 0 {
warn!("[rknpu]: ioctl received null arg pointer");
return Err(VfsError::InvalidData);
}
let nr = ioctl_nr(cmd);
info!("card0: cmd {cmd:#x}, nr {nr:#x}, arg {arg:#x}");
let is_driver_ioctl = is_driver_ioctl(ioctl_nr(cmd));
info!("card0: is_driver_ioctl = {}", is_driver_ioctl);
let mut stack_data = [0u8; 128];
let in_size = io_size(cmd) as usize;
let out_size = in_size;
copy_from_user(stack_data.as_mut_ptr(), arg as _, in_size)?;
if is_driver_ioctl {
panic!("card0: driver ioctls are not supported");
} else {
assert!(nr <= 0xcf, "card0: unsupported ioctl nr {nr}");
match nr {
0 => {
info!("drm get version");
drm_version(&mut stack_data)?;
}
_ => {
panic!("card0: unsupported ioctl nr {nr}");
}
}
}
copy_to_user(arg as _, stack_data.as_mut_ptr(), out_size)?;
Ok(0)
}
fn as_any(&self) -> &dyn Any {
self
}
fn flags(&self) -> NodeFlags {
NodeFlags::NON_CACHEABLE
}
}
unsafe fn drm_copy_field(
buf: *mut u8,
buf_len: &mut c_ulong,
value: *const u8,
) -> Result<(), VfsError> {
if value.is_null() {
warn!("[drm_copy_field] BUG: the value to copy was not set!");
*buf_len = 0;
return Ok(());
}
let mut len = 0;
unsafe {
let mut ptr = value;
while *ptr != 0 {
len += 1;
ptr = ptr.add(1);
}
}
let original_buf_len = *buf_len;
*buf_len = len;
let copy_len = if len > original_buf_len {
original_buf_len as usize
} else {
len as usize
};
if copy_len > 0 && !buf.is_null() {
copy_to_user(buf as _, value, copy_len)?;
}
Ok(())
}
fn drm_version(data: &mut [u8]) -> VfsResult<()> {
let data = unsafe { &mut *(data.as_mut_ptr() as *mut DrmVersion) };
info!("drm_version called: {:?}", data);
data.version_major = 3;
data.version_minor = 0;
data.version_patchlevel = 0;
unsafe {
let ret = drm_copy_field(data.name as *mut u8, &mut data.name_len, DRM0_NAME.as_ptr());
if let Err(e) = ret {
warn!("[drm_version] Failed to copy driver name: {:?}", e);
return Err(VfsError::InvalidData);
}
let ret = drm_copy_field(data.date as *mut u8, &mut data.date_len, DRM0_DATE.as_ptr());
if let Err(e) = ret {
warn!("[drm_version] Failed to copy driver date: {:?}", e);
return Err(VfsError::InvalidData);
}
let ret = drm_copy_field(
data.desc.cast(),
&mut data.desc_len,
DRM0_DESC.as_ptr().cast(),
);
if let Err(e) = ret {
warn!("[drm_version] Failed to copy driver description: {:?}", e);
return Err(VfsError::InvalidData);
}
}
info!(
"[drm_version] Set driver info: name_len={}, date_len={}, desc_len={}",
data.name_len, data.date_len, data.desc_len
);
Ok(())
}
pub fn copy_from_user(dst: *mut u8, src: *const u8, size: usize) -> Result<(), VfsError> {
let ret = unsafe { user_copy(dst, src, size) };
if ret != 0 {
warn!("[rknpu]: copy_from_user failed, ret={}", ret);
return Err(VfsError::InvalidData);
}
Ok(())
}
pub fn copy_to_user(dst: *mut u8, src: *const u8, size: usize) -> Result<(), VfsError> {
let ret = unsafe { user_copy(dst, src, size) };
if ret != 0 {
warn!("[rknpu]: copy_to_user failed, ret={}", ret);
return Err(VfsError::InvalidData);
}
Ok(())
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RknpuCmd {
Action = 0x00,
Submit = 0x01,
MemCreate = 0x02,
MemMap = 0x03,
MemDestroy = 0x04,
MemSync = 0x05,
}
impl TryFrom<u32> for RknpuCmd {
type Error = ();
fn try_from(nr: u32) -> Result<Self, Self::Error> {
match nr {
0x00 | 0x40 => Ok(RknpuCmd::Action),
0x01 | 0x41 => Ok(RknpuCmd::Submit),
0x02 | 0x42 => Ok(RknpuCmd::MemCreate),
0x03 | 0x43 => Ok(RknpuCmd::MemMap),
0x04 | 0x44 => Ok(RknpuCmd::MemDestroy),
0x05 | 0x45 => Ok(RknpuCmd::MemSync),
_ => {
warn!("Unknown ioctl nr: {nr:#x}",);
Err(())
}
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct DrmUnique {
pub unique_len: c_ulong,
pub unique: *mut c_char,
}
impl DrmUnique {
pub const fn new() -> Self {
Self {
unique_len: 0,
unique: core::ptr::null_mut(),
}
}
pub fn with_buffer(buffer: *mut c_char, len: c_ulong) -> Self {
Self {
unique_len: len,
unique: buffer,
}
}
pub fn is_null(&self) -> bool {
self.unique.is_null()
}
pub fn len(&self) -> c_ulong {
self.unique_len
}
pub fn set_len(&mut self, len: c_ulong) {
self.unique_len = len;
}
}