231 lines
6.7 KiB
Rust
231 lines
6.7 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use alloc::{borrow::Cow, string::ToString, sync::Arc, vec::Vec};
|
|
use core::fmt::Debug;
|
|
|
|
use aster_util::printer::VmPrinter;
|
|
use inherit_methods_macro::inherit_methods;
|
|
use ostd::{
|
|
mm::{FallibleVmRead, VmReader, VmWriter},
|
|
prelude::ktest,
|
|
};
|
|
|
|
use super::{
|
|
inherit_sys_branch_node, inherit_sys_symlink_node, BranchNodeFields, Error, Result,
|
|
SymlinkNodeFields, SysAttrSetBuilder, SysBranchNode, SysNode, SysNodeType, SysObj, SysPerms,
|
|
SysStr, SysSymlink, SysTree,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
struct DeviceNode {
|
|
fields: BranchNodeFields<dyn SysObj, Self>,
|
|
}
|
|
|
|
impl DeviceNode {
|
|
fn new(name: SysStr) -> Arc<Self> {
|
|
let mut builder = SysAttrSetBuilder::new();
|
|
builder
|
|
.add(Cow::Borrowed("model"), SysPerms::DEFAULT_RO_ATTR_PERMS)
|
|
.add(Cow::Borrowed("vendor"), SysPerms::DEFAULT_RO_ATTR_PERMS)
|
|
.add(Cow::Borrowed("status"), SysPerms::DEFAULT_RW_ATTR_PERMS);
|
|
|
|
let attrs = builder.build().expect("Failed to build attribute set");
|
|
|
|
Arc::new_cyclic(|weak_self| {
|
|
let fields = BranchNodeFields::new(name, attrs, weak_self.clone());
|
|
DeviceNode { fields }
|
|
})
|
|
}
|
|
}
|
|
|
|
#[inherit_methods(from = "self.fields")]
|
|
impl DeviceNode {
|
|
pub fn add_child(&self, new_child: Arc<dyn SysObj>) -> Result<()>;
|
|
}
|
|
|
|
inherit_sys_branch_node!(DeviceNode, fields, {
|
|
fn read_attr_at(&self, name: &str, offset: usize, writer: &mut VmWriter) -> Result<usize> {
|
|
// Check if attribute exists
|
|
if !self.fields.attr_set().contains(name) {
|
|
return Err(Error::NotFound);
|
|
}
|
|
|
|
let attr = self.fields.attr_set().get(name).unwrap();
|
|
// Check if attribute is readable
|
|
if !attr.perms().can_read() {
|
|
return Err(Error::PermissionDenied);
|
|
}
|
|
let value = match name {
|
|
"model" => "MyDevice",
|
|
"vendor" => "ExampleVendor",
|
|
"status" => "online",
|
|
_ => "",
|
|
};
|
|
|
|
let mut printer = VmPrinter::new_skip(writer, offset);
|
|
// Write the value to the provided writer
|
|
write!(printer, "{}", value)?;
|
|
|
|
Ok(printer.bytes_written())
|
|
}
|
|
|
|
fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result<usize> {
|
|
// Get attribute and check if it exists
|
|
let attr = self.fields.attr_set().get(name).ok_or(Error::NotFound)?;
|
|
|
|
// Check if attribute is writable
|
|
if !attr.perms().can_write() {
|
|
return Err(Error::PermissionDenied);
|
|
}
|
|
|
|
// Read new value from the provided reader
|
|
let mut buffer = [0u8; 256];
|
|
let mut writer = VmWriter::from(&mut buffer[..]);
|
|
let read_len = reader
|
|
.read_fallible(&mut writer)
|
|
.map_err(|_| Error::PageFault)?;
|
|
|
|
Ok(read_len)
|
|
}
|
|
|
|
fn perms(&self) -> SysPerms {
|
|
SysPerms::DEFAULT_RW_PERMS
|
|
}
|
|
});
|
|
|
|
#[derive(Debug)]
|
|
struct SymlinkNode {
|
|
fields: SymlinkNodeFields<Self>,
|
|
}
|
|
|
|
impl SymlinkNode {
|
|
fn new(name: SysStr, target: &str) -> Arc<Self> {
|
|
Arc::new_cyclic(|weak_self| {
|
|
let fields = SymlinkNodeFields::new(name, target.to_string(), weak_self.clone());
|
|
SymlinkNode { fields }
|
|
})
|
|
}
|
|
}
|
|
|
|
inherit_sys_symlink_node!(SymlinkNode, fields);
|
|
|
|
#[ktest]
|
|
fn systree_singleton() {
|
|
// Get the SysTree singleton
|
|
let sys_tree = SysTree::new();
|
|
|
|
// Access the root node
|
|
let root = sys_tree.root();
|
|
|
|
// Check if root node exists
|
|
assert!(root.is_root());
|
|
assert_eq!(root.name(), "");
|
|
assert_eq!(root.type_(), SysNodeType::Branch);
|
|
}
|
|
|
|
#[ktest]
|
|
fn node_path() {
|
|
let sys_tree = SysTree::new();
|
|
let root = sys_tree.root();
|
|
|
|
assert_eq!(root.path(), "/");
|
|
|
|
let device = DeviceNode::new("device".into());
|
|
let sub_device_1 = DeviceNode::new("sub_device_1".into());
|
|
// Add the child node to `device` before attaching it to the `SysTree`.
|
|
device.add_child(sub_device_1.clone()).unwrap();
|
|
root.add_child(device.clone()).unwrap();
|
|
|
|
assert_eq!(device.path(), "/device");
|
|
assert_eq!(sub_device_1.path(), "/device/sub_device_1");
|
|
|
|
let sub_device_2 = DeviceNode::new("sub_device_2".into());
|
|
// Add the child node to `device` after attaching it to the `SysTree`.
|
|
device.add_child(sub_device_2.clone()).unwrap();
|
|
assert_eq!(sub_device_2.path(), "/device/sub_device_2");
|
|
}
|
|
|
|
#[ktest]
|
|
fn node_hierarchy() {
|
|
// Create device node hierarchy
|
|
let root_device = DeviceNode::new("root_device".into());
|
|
|
|
// Add child nodes
|
|
{
|
|
let child1 = DeviceNode::new("child1".into());
|
|
let child2 = DeviceNode::new("child2".into());
|
|
root_device.add_child(child1).unwrap();
|
|
root_device.add_child(child2).unwrap();
|
|
}
|
|
|
|
// Verify number of child nodes
|
|
assert_eq!(root_device.count_children(), 2);
|
|
|
|
// Get specific child node
|
|
let child = root_device.child("child1").unwrap();
|
|
assert_eq!(child.name(), "child1");
|
|
|
|
// Traverse all child nodes
|
|
let all_children: Vec<_> = root_device.children();
|
|
assert_eq!(all_children.len(), 2);
|
|
}
|
|
|
|
#[ktest]
|
|
fn attributes() {
|
|
let device = DeviceNode::new("test_device".into());
|
|
|
|
// Read read-only attribute
|
|
let model = device.show_attr("model").unwrap();
|
|
assert_eq!(model, "MyDevice");
|
|
|
|
// Read read-write attribute
|
|
let status = device.show_attr("status").unwrap();
|
|
assert_eq!(status, "online");
|
|
|
|
// Modify read-write attribute
|
|
let len = device.store_attr("status", "offline").unwrap();
|
|
assert_eq!(len, 7);
|
|
|
|
// Attempt to modify read-only attribute (should fail)
|
|
let result = device.store_attr("model", "NewModel");
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[ktest]
|
|
fn symlinks() {
|
|
let device = DeviceNode::new("device".into());
|
|
|
|
// Create symlink pointing to device
|
|
let symlink = SymlinkNode::new("device_link".into(), "/sys/devices/device");
|
|
|
|
// Verify symlink attributes
|
|
assert_eq!(symlink.type_(), SysNodeType::Symlink);
|
|
assert_eq!(symlink.target_path(), "/sys/devices/device");
|
|
|
|
// Add symlink to device tree
|
|
{
|
|
device.add_child(symlink.clone()).unwrap();
|
|
}
|
|
|
|
// Verify symlink was added correctly
|
|
let symlink_obj = device.child("device_link").unwrap();
|
|
let symlink_node = symlink_obj.cast_to_symlink().unwrap();
|
|
assert_eq!(symlink_node.target_path(), "/sys/devices/device");
|
|
}
|
|
|
|
#[ktest]
|
|
fn error_handling() {
|
|
let device = DeviceNode::new("error_test".into());
|
|
|
|
// Attempt to access non-existent attribute
|
|
let result = device.show_attr("nonexistent");
|
|
match result {
|
|
Err(Error::NotFound) => (),
|
|
_ => panic!("Failed to handle non-existent attribute error"),
|
|
}
|
|
|
|
// Attempt to access non-existent child node
|
|
let child = device.child("nonexistent");
|
|
assert!(child.is_none());
|
|
}
|