Files
asterinas/services/libs/jinux-std/src/fs/device.rs
2023-05-29 19:02:35 +08:00

144 lines
4.1 KiB
Rust

use crate::fs::fs_resolver::{FsPath, FsResolver};
use crate::fs::utils::Dentry;
use crate::fs::utils::{InodeMode, InodeType, IoEvents, IoctlCmd, Poller};
use crate::prelude::*;
/// The abstract of device
pub trait Device: Sync + Send {
/// Return the device type.
fn type_(&self) -> DeviceType;
/// Return the device ID.
fn id(&self) -> DeviceId;
/// Read from the device.
fn read(&self, buf: &mut [u8]) -> Result<usize>;
/// Write to the device.
fn write(&self, buf: &[u8]) -> Result<usize>;
/// Poll on the device.
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}
/// Ioctl on the device.
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
return_errno_with_message!(Errno::EINVAL, "ioctl is not supported");
}
}
/// Device type
pub enum DeviceType {
CharDevice,
BlockDevice,
}
/// Device Id
pub struct DeviceId(u64);
impl DeviceId {
pub fn new(major: u32, minor: u32) -> Self {
let major = major as u64;
let minor = minor as u64;
Self(
(major & 0xffff_f000) << 32
| (major & 0x0000_0fff) << 8
| (minor & 0xffff_ff00) << 12
| (minor & 0x0000_00ff),
)
}
pub fn major(&self) -> u32 {
((self.0 >> 32) & 0xffff_f000 | (self.0 >> 8) & 0x0000_0fff) as u32
}
pub fn minor(&self) -> u32 {
((self.0 >> 12) & 0xffff_ff00 | self.0 & 0x0000_00ff) as u32
}
}
impl Into<u64> for DeviceId {
fn into(self) -> u64 {
self.0
}
}
/// Add a device node to FS for the device.
///
/// If the parent path is not existing, `mkdir -p` the parent path.
/// This function is used in registering device.
pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Arc<Dentry>> {
let mut dentry = {
let fs_resolver = FsResolver::new();
fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())?
};
let mut relative_path = {
let relative_path = path.trim_start_matches('/');
if relative_path.is_empty() {
return_errno_with_message!(Errno::EINVAL, "invalid device path");
}
relative_path
};
while !relative_path.is_empty() {
let (next_name, path_remain) = if let Some((prefix, suffix)) = relative_path.split_once('/')
{
(prefix, suffix.trim_start_matches('/'))
} else {
(relative_path, "")
};
match dentry.lookup(next_name) {
Ok(next_dentry) => {
if path_remain.is_empty() {
return_errno_with_message!(Errno::EEXIST, "device node is existing");
}
dentry = next_dentry;
}
Err(_) => {
if path_remain.is_empty() {
// Create the device node
dentry = dentry.mknod(
next_name,
InodeMode::from_bits_truncate(0o666),
device.clone(),
)?;
} else {
// Mkdir parent path
dentry = dentry.create(
next_name,
InodeType::Dir,
InodeMode::from_bits_truncate(0o755),
)?;
}
}
}
relative_path = path_remain;
}
Ok(dentry)
}
/// Delete the device node from FS for the device.
///
/// This function is used in unregistering device.
pub fn delete_node(path: &str) -> Result<()> {
let abs_path = {
let device_path = path.trim_start_matches('/');
if device_path.is_empty() {
return_errno_with_message!(Errno::EINVAL, "invalid device path");
}
String::from("/dev") + "/" + device_path
};
let (parent_dentry, name) = {
let fs_resolver = FsResolver::new();
fs_resolver.lookup_dir_and_base_name(&FsPath::try_from(abs_path.as_str()).unwrap())?
};
parent_dentry.unlink(&name)?;
Ok(())
}