104 lines
3.1 KiB
Rust
104 lines
3.1 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use device_id::DeviceId;
|
|
|
|
use super::inode_handle::FileIo;
|
|
use crate::{
|
|
fs::{
|
|
fs_resolver::{FsPath, FsResolver},
|
|
path::Path,
|
|
utils::{mkmod, InodeType, MknodType},
|
|
},
|
|
prelude::*,
|
|
};
|
|
|
|
/// The abstraction of a device.
|
|
pub trait Device: Send + Sync + 'static {
|
|
/// Returns the device type.
|
|
fn type_(&self) -> DeviceType;
|
|
|
|
/// Returns the device ID.
|
|
fn id(&self) -> DeviceId;
|
|
|
|
/// Returns the path where the device should appear in devtmpfs (usually under `/dev`), if any.
|
|
fn devtmpfs_path(&self) -> Option<String>;
|
|
|
|
/// Opens the device, returning a file-like object that the userspace can interact with by
|
|
/// doing I/O.
|
|
fn open(&self) -> Result<Box<dyn FileIo>>;
|
|
}
|
|
|
|
impl Debug for dyn Device {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
f.debug_struct("Device")
|
|
.field("type", &self.type_())
|
|
.field("id", &self.id())
|
|
.field("devtmpfs_path", &self.devtmpfs_path())
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
/// Device type
|
|
#[derive(Debug)]
|
|
pub enum DeviceType {
|
|
Char,
|
|
Block,
|
|
}
|
|
|
|
/// Adds a device node in `/dev`.
|
|
///
|
|
/// If the parent path does not exist, it will be created as a directory.
|
|
/// This function should be called when registering a device.
|
|
//
|
|
// TODO: Figure out what should happen when unregistering the device.
|
|
pub fn add_node(
|
|
dev_type: DeviceType,
|
|
dev_id: u64,
|
|
path: &str,
|
|
fs_resolver: &FsResolver,
|
|
) -> Result<Path> {
|
|
let mut dev_path = 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, "the device path is invalid");
|
|
}
|
|
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 dev_path.lookup(next_name) {
|
|
Ok(next_path) => {
|
|
if path_remain.is_empty() {
|
|
return_errno_with_message!(Errno::EEXIST, "the device node already exists");
|
|
}
|
|
dev_path = next_path;
|
|
}
|
|
Err(_) => {
|
|
if path_remain.is_empty() {
|
|
// Create the device node
|
|
let mknod_type = match dev_type {
|
|
DeviceType::Block => MknodType::BlockDevice(dev_id),
|
|
DeviceType::Char => MknodType::CharDevice(dev_id),
|
|
};
|
|
dev_path = dev_path.mknod(next_name, mkmod!(a+rw), mknod_type)?;
|
|
} else {
|
|
// Create the parent directory
|
|
dev_path =
|
|
dev_path.new_fs_child(next_name, InodeType::Dir, mkmod!(a+rx, u+w))?;
|
|
}
|
|
}
|
|
}
|
|
relative_path = path_remain;
|
|
}
|
|
|
|
Ok(dev_path)
|
|
}
|