Add runtime tools and Red Bear service wiring
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Subsystem {
|
||||
Gpu,
|
||||
Network,
|
||||
@@ -9,8 +9,16 @@ pub enum Subsystem {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum InputKind {
|
||||
Keyboard,
|
||||
Mouse,
|
||||
Generic,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeviceInfo {
|
||||
pub is_pci: bool,
|
||||
pub bus: u8,
|
||||
pub dev: u8,
|
||||
pub func: u8,
|
||||
@@ -19,15 +27,89 @@ pub struct DeviceInfo {
|
||||
pub class_code: u8,
|
||||
pub subclass: u8,
|
||||
pub subsystem: Subsystem,
|
||||
pub input_kind: Option<InputKind>,
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub devpath: String,
|
||||
pub devnode: String,
|
||||
pub scheme_target: String,
|
||||
pub symlinks: Vec<String>,
|
||||
}
|
||||
|
||||
impl DeviceInfo {
|
||||
pub fn new_platform_input(
|
||||
name: &str,
|
||||
devpath: &str,
|
||||
input_kind: InputKind,
|
||||
devnode: &str,
|
||||
scheme_target: &str,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_pci: false,
|
||||
bus: 0,
|
||||
dev: 0,
|
||||
func: 0,
|
||||
vendor_id: 0,
|
||||
device_id: 0,
|
||||
class_code: 0,
|
||||
subclass: 0,
|
||||
subsystem: Subsystem::Input,
|
||||
input_kind: Some(input_kind),
|
||||
name: name.to_string(),
|
||||
devpath: devpath.to_string(),
|
||||
devnode: devnode.to_string(),
|
||||
scheme_target: scheme_target.to_string(),
|
||||
symlinks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_node_metadata(
|
||||
&mut self,
|
||||
devnode: impl Into<String>,
|
||||
scheme_target: impl Into<String>,
|
||||
symlinks: Vec<String>,
|
||||
) {
|
||||
self.devnode = devnode.into();
|
||||
self.scheme_target = scheme_target.into();
|
||||
self.symlinks = symlinks;
|
||||
}
|
||||
|
||||
pub fn subsystem_name(&self) -> &'static str {
|
||||
match self.subsystem {
|
||||
Subsystem::Gpu => "drm",
|
||||
Subsystem::Network => "net",
|
||||
Subsystem::Storage => "block",
|
||||
Subsystem::Audio => "sound",
|
||||
Subsystem::Usb => "usb",
|
||||
Subsystem::Input => "input",
|
||||
Subsystem::Unknown => "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_path(&self) -> String {
|
||||
if let Some(slot) = self.devpath.strip_prefix("/devices/pci/") {
|
||||
return format!("pci-{slot}");
|
||||
}
|
||||
|
||||
self.devpath
|
||||
.trim_start_matches("/devices/")
|
||||
.replace('/', "-")
|
||||
}
|
||||
|
||||
pub fn is_input_keyboard(&self) -> bool {
|
||||
self.input_kind == Some(InputKind::Keyboard)
|
||||
}
|
||||
|
||||
pub fn is_input_mouse(&self) -> bool {
|
||||
self.input_kind == Some(InputKind::Mouse)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn classify_pci_device(bus: u8, dev: u8, func: u8) -> DeviceInfo {
|
||||
let path = format!("/devices/pci/{:04x}:{:02x}:{:02x}.{}", bus, 0, dev, func);
|
||||
let devpath = format!("/devices/pci/{:04x}:{:02x}:{:02x}.{}", bus, 0, dev, func);
|
||||
|
||||
let config_path = format!("/scheme/pci/{}.{}.{}", bus, dev, func);
|
||||
let (vendor_id, device_id, class_code, subclass) = read_pci_config(&config_path);
|
||||
let input_kind = detect_input_kind(class_code, subclass);
|
||||
|
||||
let subsystem = match class_code {
|
||||
0x03 => Subsystem::Gpu,
|
||||
@@ -39,9 +121,10 @@ pub fn classify_pci_device(bus: u8, dev: u8, func: u8) -> DeviceInfo {
|
||||
_ => Subsystem::Unknown,
|
||||
};
|
||||
|
||||
let name = format_device_name(vendor_id, device_id, class_code);
|
||||
let name = format_device_name(vendor_id, device_id, class_code, subclass, input_kind);
|
||||
|
||||
DeviceInfo {
|
||||
is_pci: true,
|
||||
bus,
|
||||
dev,
|
||||
func,
|
||||
@@ -50,8 +133,12 @@ pub fn classify_pci_device(bus: u8, dev: u8, func: u8) -> DeviceInfo {
|
||||
class_code,
|
||||
subclass,
|
||||
subsystem,
|
||||
input_kind,
|
||||
name,
|
||||
path,
|
||||
devpath,
|
||||
devnode: String::new(),
|
||||
scheme_target: String::new(),
|
||||
symlinks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +155,40 @@ fn read_pci_config(path: &str) -> (u16, u16, u8, u8) {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_device_name(vendor_id: u16, device_id: u16, class_code: u8) -> String {
|
||||
fn detect_input_kind(class_code: u8, subclass: u8) -> Option<InputKind> {
|
||||
if class_code != 0x09 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match subclass {
|
||||
0x00 => Some(InputKind::Keyboard),
|
||||
0x04 => Some(InputKind::Generic),
|
||||
_ => Some(InputKind::Generic),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_device_name(
|
||||
vendor_id: u16,
|
||||
device_id: u16,
|
||||
class_code: u8,
|
||||
subclass: u8,
|
||||
input_kind: Option<InputKind>,
|
||||
) -> String {
|
||||
if class_code == 0x03 {
|
||||
if let Some(name) = gpu_device_name(vendor_id, device_id) {
|
||||
return format!("{name} [{vendor_id:04x}:{device_id:04x}]");
|
||||
}
|
||||
}
|
||||
|
||||
if class_code == 0x09 {
|
||||
let name = match (subclass, input_kind) {
|
||||
(0x00, Some(InputKind::Keyboard)) => "PS/2 Keyboard Controller",
|
||||
(0x04, _) => "USB HID Controller",
|
||||
_ => "Input Device",
|
||||
};
|
||||
return format!("{name} [{vendor_id:04x}:{device_id:04x}]");
|
||||
}
|
||||
|
||||
let vendor_name = match vendor_id {
|
||||
0x8086 => "Intel",
|
||||
0x1002 => "AMD",
|
||||
@@ -95,19 +215,93 @@ fn format_device_name(vendor_id: u16, device_id: u16, class_code: u8) -> String
|
||||
)
|
||||
}
|
||||
|
||||
pub fn format_device_info(dev: &DeviceInfo) -> String {
|
||||
let subsystem = match dev.subsystem {
|
||||
Subsystem::Gpu => "gpu",
|
||||
Subsystem::Network => "net",
|
||||
Subsystem::Storage => "block",
|
||||
Subsystem::Audio => "sound",
|
||||
Subsystem::Usb => "usb",
|
||||
Subsystem::Input => "input",
|
||||
Subsystem::Unknown => "unknown",
|
||||
};
|
||||
|
||||
format!(
|
||||
"P={}\nE=SUBSYSTEM={}\nE=PCI_VENDOR_ID={:#06x}\nE=PCI_DEVICE_ID={:#06x}\nE=PCI_CLASS={:#04x}{:02x}\nE=DEVNAME={}\n",
|
||||
dev.path, subsystem, dev.vendor_id, dev.device_id, dev.class_code, dev.subclass, dev.name
|
||||
)
|
||||
fn gpu_device_name(vendor_id: u16, device_id: u16) -> Option<&'static str> {
|
||||
match vendor_id {
|
||||
0x1002 => match device_id {
|
||||
0x73A3 => Some("AMD Radeon RX 6600 XT / 6650 XT (RDNA2)"),
|
||||
0x73BF => Some("AMD Radeon RX 6800 XT / 6900 XT (RDNA2)"),
|
||||
0x73DF => Some("AMD Radeon RX 6700 XT / 6750 XT (RDNA2)"),
|
||||
0x73EF => Some("AMD Radeon RX 6800 / 6850M XT (RDNA2)"),
|
||||
0x7422 => Some("AMD Radeon 780M (RDNA3)"),
|
||||
0x7448 => Some("AMD Radeon RX 7900 XT (RDNA3)"),
|
||||
0x744C => Some("AMD Radeon RX 7900 XTX (RDNA3)"),
|
||||
0x7480 => Some("AMD Radeon RX 7800 XT / 7700 XT (RDNA3)"),
|
||||
_ => Some("AMD Radeon GPU"),
|
||||
},
|
||||
0x8086 => match device_id {
|
||||
0x3E92 => Some("Intel UHD Graphics 630"),
|
||||
0x5912 => Some("Intel HD Graphics 630"),
|
||||
0x9A49 => Some("Intel Iris Xe Graphics (Tiger Lake)"),
|
||||
0x46A6 => Some("Intel Iris Xe Graphics (Alder Lake-P)"),
|
||||
0x56A0 => Some("Intel Arc Graphics (DG2)"),
|
||||
0x56A1 => Some("Intel Arc A380 (DG2)"),
|
||||
_ => Some("Intel Graphics"),
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_properties(dev: &DeviceInfo) -> Vec<(String, String)> {
|
||||
let mut props = Vec::new();
|
||||
props.push(("DEVPATH".to_string(), dev.devpath.clone()));
|
||||
props.push(("SUBSYSTEM".to_string(), dev.subsystem_name().to_string()));
|
||||
props.push(("ID_MODEL_FROM_DATABASE".to_string(), dev.name.clone()));
|
||||
|
||||
if !dev.devnode.is_empty() {
|
||||
props.push(("DEVNAME".to_string(), dev.devnode.clone()));
|
||||
}
|
||||
|
||||
let id_path = dev.id_path();
|
||||
if !id_path.is_empty() {
|
||||
props.push(("ID_PATH".to_string(), id_path));
|
||||
}
|
||||
|
||||
if dev.is_pci {
|
||||
props.push((
|
||||
"PCI_VENDOR_ID".to_string(),
|
||||
format!("0x{:04x}", dev.vendor_id),
|
||||
));
|
||||
props.push((
|
||||
"PCI_DEVICE_ID".to_string(),
|
||||
format!("0x{:04x}", dev.device_id),
|
||||
));
|
||||
props.push((
|
||||
"PCI_CLASS".to_string(),
|
||||
format!("0x{:02x}{:02x}", dev.class_code, dev.subclass),
|
||||
));
|
||||
}
|
||||
|
||||
if dev.subsystem == Subsystem::Input {
|
||||
props.push(("ID_INPUT".to_string(), "1".to_string()));
|
||||
match dev.input_kind {
|
||||
Some(InputKind::Keyboard) => {
|
||||
props.push(("ID_INPUT_KEYBOARD".to_string(), "1".to_string()));
|
||||
}
|
||||
Some(InputKind::Mouse) => {
|
||||
props.push(("ID_INPUT_MOUSE".to_string(), "1".to_string()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
props
|
||||
}
|
||||
|
||||
pub fn format_device_info(dev: &DeviceInfo) -> String {
|
||||
let mut info = format!("P={}\n", dev.devpath);
|
||||
for (key, value) in device_properties(dev) {
|
||||
info.push_str(&format!("E={key}={value}\n"));
|
||||
}
|
||||
for link in &dev.symlinks {
|
||||
info.push_str(&format!("S={}\n", link.trim_start_matches('/')));
|
||||
}
|
||||
info
|
||||
}
|
||||
|
||||
pub fn format_uevent_info(dev: &DeviceInfo) -> String {
|
||||
let mut info = String::from("ACTION=add\n");
|
||||
for (key, value) in device_properties(dev) {
|
||||
info.push_str(&format!("{key}={value}\n"));
|
||||
}
|
||||
info
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ mod device_db;
|
||||
mod scheme;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
|
||||
use log::{error, info, LevelFilter, Metadata, Record};
|
||||
use redox_scheme::{SignalBehavior, Socket};
|
||||
use redox_scheme::{
|
||||
scheme::{SchemeState, SchemeSync},
|
||||
SignalBehavior, Socket,
|
||||
};
|
||||
|
||||
use scheme::UdevScheme;
|
||||
|
||||
@@ -25,45 +28,35 @@ impl log::Log for StderrLogger {
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let mut scheme = UdevScheme::new();
|
||||
|
||||
match scheme.scan_pci_devices() {
|
||||
Ok(n) => info!("udev-shim: enumerated {} PCI device(s)", n),
|
||||
Err(e) => error!("udev-shim: PCI scan failed: {}", e),
|
||||
fn init_logging(level: LevelFilter) {
|
||||
if log::set_boxed_logger(Box::new(StderrLogger { level })).is_err() {
|
||||
return;
|
||||
}
|
||||
log::set_max_level(level);
|
||||
}
|
||||
|
||||
let socket =
|
||||
Socket::create("udev").map_err(|e| format!("failed to register udev scheme: {}", e))?;
|
||||
info!("udev-shim: registered scheme:udev");
|
||||
unsafe fn get_init_notify_fd() -> RawFd {
|
||||
let fd: RawFd = env::var("INIT_NOTIFY")
|
||||
.expect("udev-shim: INIT_NOTIFY not set")
|
||||
.parse()
|
||||
.expect("udev-shim: INIT_NOTIFY is not a valid fd");
|
||||
libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC);
|
||||
fd
|
||||
}
|
||||
|
||||
loop {
|
||||
let request = match socket.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(r)) => r,
|
||||
Ok(None) => {
|
||||
info!("udev-shim: scheme unmounted, exiting");
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("udev-shim: failed to read scheme request: {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut UdevScheme) {
|
||||
let cap_id = scheme.scheme_root().expect("udev-shim: scheme_root failed");
|
||||
let cap_fd = socket
|
||||
.create_this_scheme_fd(0, cap_id, 0, 0)
|
||||
.expect("udev-shim: create_this_scheme_fd failed");
|
||||
|
||||
let response = match request.handle_scheme_block_mut(&mut scheme) {
|
||||
Ok(r) => r,
|
||||
Err(_req) => {
|
||||
error!("udev-shim: failed to handle request");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
|
||||
error!("udev-shim: failed to write response: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
syscall::call_wo(
|
||||
notify_fd as usize,
|
||||
&libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(),
|
||||
syscall::CallFlags::FD,
|
||||
&[],
|
||||
)
|
||||
.expect("udev-shim: failed to notify init that scheme is ready");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -72,11 +65,40 @@ fn main() {
|
||||
Ok("trace") => LevelFilter::Trace,
|
||||
_ => LevelFilter::Info,
|
||||
};
|
||||
let _ = log::set_boxed_logger(Box::new(StderrLogger { level: log_level }));
|
||||
log::set_max_level(log_level);
|
||||
|
||||
if let Err(e) = run() {
|
||||
error!("udev-shim: fatal error: {}", e);
|
||||
process::exit(1);
|
||||
init_logging(log_level);
|
||||
|
||||
let mut scheme = UdevScheme::new();
|
||||
|
||||
match scheme.scan_pci_devices() {
|
||||
Ok(n) => info!("udev-shim: enumerated {} PCI device(s)", n),
|
||||
Err(e) => error!("udev-shim: PCI scan failed: {}", e),
|
||||
}
|
||||
|
||||
let notify_fd = unsafe { get_init_notify_fd() };
|
||||
let socket = Socket::create().expect("udev-shim: failed to create udev scheme");
|
||||
let mut state = SchemeState::new();
|
||||
|
||||
notify_scheme_ready(notify_fd, &socket, &mut scheme);
|
||||
|
||||
libredox::call::setrens(0, 0).expect("udev-shim: failed to enter null namespace");
|
||||
|
||||
info!("udev-shim: registered scheme:udev");
|
||||
|
||||
while let Some(request) = socket
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("udev-shim: failed to read scheme request")
|
||||
{
|
||||
match request.kind() {
|
||||
redox_scheme::RequestKind::Call(request) => {
|
||||
let response = request.handle_sync(&mut scheme, &mut state);
|
||||
socket
|
||||
.write_response(response, SignalBehavior::Restart)
|
||||
.expect("udev-shim: failed to write response");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@@ -1,170 +1,633 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EROFS};
|
||||
use syscall::flag::{EventFlags, MODE_DIR, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use syscall::error::{Error, Result, EACCES, EBADF, ENOENT, EROFS};
|
||||
use syscall::flag::{EventFlags, MODE_DIR, MODE_FILE};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
|
||||
use crate::device_db::{classify_pci_device, format_device_info, DeviceInfo, Subsystem};
|
||||
use crate::device_db::{
|
||||
classify_pci_device, format_device_info, format_uevent_info, DeviceInfo, InputKind, Subsystem,
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
kind: HandleKind,
|
||||
offset: usize,
|
||||
}
|
||||
const SCHEME_ROOT_ID: usize = 1;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum HandleKind {
|
||||
Root,
|
||||
Devices,
|
||||
Device(usize),
|
||||
Dev,
|
||||
DevInputDir,
|
||||
DevInput(usize),
|
||||
DevInputMice,
|
||||
DevDriDir,
|
||||
DevDri(usize),
|
||||
DevLinks,
|
||||
LinksInputDir,
|
||||
LinksInputByPathDir,
|
||||
LinksDriDir,
|
||||
LinksDriByPathDir,
|
||||
Link(usize),
|
||||
Uevent,
|
||||
}
|
||||
|
||||
pub struct UdevScheme {
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
handles: BTreeMap<usize, HandleKind>,
|
||||
devices: Vec<DeviceInfo>,
|
||||
}
|
||||
|
||||
impl UdevScheme {
|
||||
pub fn new() -> Self {
|
||||
UdevScheme {
|
||||
next_id: 0,
|
||||
Self {
|
||||
next_id: SCHEME_ROOT_ID + 1,
|
||||
handles: BTreeMap::new(),
|
||||
devices: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_pci_devices(&mut self) -> Result<usize> {
|
||||
let dir = match std::fs::read_dir("/scheme/pci") {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
log::warn!("udev-shim: failed to read /scheme/pci: {e}");
|
||||
return Ok(0);
|
||||
self.devices.clear();
|
||||
|
||||
let mut pci_slots = Vec::new();
|
||||
match std::fs::read_dir("/scheme/pci") {
|
||||
Ok(dir) => {
|
||||
for entry in dir {
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let name = match entry.file_name().to_str() {
|
||||
Some(name) => name.to_string(),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if let Some(slot) = parse_pci_slot(&name) {
|
||||
pci_slots.push(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut count = 0;
|
||||
for entry in dir {
|
||||
let entry = match entry {
|
||||
Ok(e) => e,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let name = match entry.file_name().to_str() {
|
||||
Some(n) => n.to_string(),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let parts: Vec<&str> = name.split('.').collect();
|
||||
if parts.len() < 3 {
|
||||
continue;
|
||||
Err(err) => {
|
||||
log::warn!("udev-shim: failed to read /scheme/pci: {err}");
|
||||
}
|
||||
|
||||
let bus: u8 = parts[0].parse().unwrap_or(0);
|
||||
let dev: u8 = parts[1].parse().unwrap_or(0);
|
||||
let func: u8 = parts[2].parse().unwrap_or(0);
|
||||
|
||||
let info = classify_pci_device(bus, dev, func);
|
||||
self.devices.push(info);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
pci_slots.sort_unstable();
|
||||
for (bus, dev, func) in pci_slots {
|
||||
self.devices.push(classify_pci_device(bus, dev, func));
|
||||
}
|
||||
|
||||
if path_exists("/scheme/input") {
|
||||
self.devices.push(DeviceInfo::new_platform_input(
|
||||
"Redox Keyboard Input",
|
||||
"/devices/platform/keyboard0",
|
||||
InputKind::Keyboard,
|
||||
"",
|
||||
"",
|
||||
));
|
||||
}
|
||||
|
||||
if path_exists("/scheme/pointer") || path_exists("/scheme/mouse") {
|
||||
self.devices.push(DeviceInfo::new_platform_input(
|
||||
"Redox Mouse Input",
|
||||
"/devices/platform/mouse0",
|
||||
InputKind::Mouse,
|
||||
"",
|
||||
"",
|
||||
));
|
||||
}
|
||||
|
||||
self.assign_virtual_nodes();
|
||||
|
||||
Ok(self.devices.len())
|
||||
}
|
||||
|
||||
fn assign_virtual_nodes(&mut self) {
|
||||
for dev in &mut self.devices {
|
||||
if dev.subsystem == Subsystem::Gpu || (dev.subsystem == Subsystem::Input && !dev.is_pci)
|
||||
{
|
||||
dev.set_node_metadata("", "", Vec::new());
|
||||
} else {
|
||||
dev.symlinks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
let mut gpu_indices: Vec<usize> = self
|
||||
.devices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, dev)| (dev.subsystem == Subsystem::Gpu).then_some(idx))
|
||||
.collect();
|
||||
gpu_indices.sort_by_key(|idx| {
|
||||
let dev = &self.devices[*idx];
|
||||
(gpu_priority(dev), dev.bus, dev.dev, dev.func)
|
||||
});
|
||||
|
||||
for (card_idx, device_idx) in gpu_indices.into_iter().enumerate() {
|
||||
let devnode = format!("/dev/dri/card{card_idx}");
|
||||
let scheme_target = format!("drm/card{card_idx}");
|
||||
let symlink = format!(
|
||||
"/links/dri/by-path/{}-card",
|
||||
self.devices[device_idx].id_path()
|
||||
);
|
||||
self.devices[device_idx].set_node_metadata(devnode, scheme_target, vec![symlink]);
|
||||
}
|
||||
|
||||
let mut input_indices: Vec<usize> = self
|
||||
.devices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, dev)| {
|
||||
(dev.subsystem == Subsystem::Input && !dev.is_pci).then_some(idx)
|
||||
})
|
||||
.collect();
|
||||
input_indices.sort_by_key(|idx| {
|
||||
let dev = &self.devices[*idx];
|
||||
(input_priority(dev), dev.devpath.clone())
|
||||
});
|
||||
|
||||
for (event_idx, device_idx) in input_indices.into_iter().enumerate() {
|
||||
let devnode = format!("/dev/input/event{event_idx}");
|
||||
let scheme_target = format!("evdev/event{event_idx}");
|
||||
let suffix = match self.devices[device_idx].input_kind {
|
||||
Some(InputKind::Keyboard) => "event-kbd",
|
||||
Some(InputKind::Mouse) => "event-mouse",
|
||||
Some(InputKind::Generic) | None => "event",
|
||||
};
|
||||
let symlink = format!(
|
||||
"/links/input/by-path/{}-{}",
|
||||
self.devices[device_idx].id_path(),
|
||||
suffix
|
||||
);
|
||||
self.devices[device_idx].set_node_metadata(devnode, scheme_target, vec![symlink]);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_device_by_devnode(&self, devnode: &str) -> Option<usize> {
|
||||
self.devices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(idx, dev)| (dev.devnode == devnode).then_some(idx))
|
||||
}
|
||||
|
||||
fn find_device_by_link(&self, prefix: &str, tail: &str) -> Option<usize> {
|
||||
let expected = format!("{prefix}{tail}");
|
||||
self.devices.iter().enumerate().find_map(|(idx, dev)| {
|
||||
dev.symlinks
|
||||
.iter()
|
||||
.any(|link| link == &expected)
|
||||
.then_some(idx)
|
||||
})
|
||||
}
|
||||
|
||||
fn mouse_device_index(&self) -> Option<usize> {
|
||||
self.devices.iter().enumerate().find_map(|(idx, dev)| {
|
||||
(dev.is_input_mouse() && !dev.scheme_target.is_empty()).then_some(idx)
|
||||
})
|
||||
}
|
||||
|
||||
fn input_event_indices(&self) -> Vec<usize> {
|
||||
self.devices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, dev)| {
|
||||
(dev.devnode.starts_with("/dev/input/event") && !dev.scheme_target.is_empty())
|
||||
.then_some(idx)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn dri_card_indices(&self) -> Vec<usize> {
|
||||
self.devices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, dev)| {
|
||||
(dev.devnode.starts_with("/dev/dri/card") && !dev.scheme_target.is_empty())
|
||||
.then_some(idx)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn directory_listing<I>(&self, entries: I) -> String
|
||||
where
|
||||
I: IntoIterator<Item = String>,
|
||||
{
|
||||
let mut listing = String::new();
|
||||
for entry in entries {
|
||||
listing.push_str(&entry);
|
||||
listing.push('\n');
|
||||
}
|
||||
listing
|
||||
}
|
||||
|
||||
fn link_listing(&self, prefix: &str) -> String {
|
||||
self.directory_listing(
|
||||
self.devices
|
||||
.iter()
|
||||
.flat_map(|dev| dev.symlinks.iter())
|
||||
.filter_map(|link| {
|
||||
link.strip_prefix(prefix).and_then(|tail| {
|
||||
(!tail.is_empty() && !tail.contains('/')).then(|| tail.to_string())
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn uevent_content(&self) -> String {
|
||||
let mut content = String::new();
|
||||
for (idx, dev) in self.devices.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
content.push('\n');
|
||||
}
|
||||
content.push_str(&format_uevent_info(dev));
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
fn content_for_handle(&self, kind: &HandleKind) -> Result<String> {
|
||||
match kind {
|
||||
HandleKind::Root => Ok(self.directory_listing(
|
||||
["devices", "dev", "links", "uevent"]
|
||||
.into_iter()
|
||||
.map(String::from),
|
||||
)),
|
||||
HandleKind::Devices => {
|
||||
Ok(self.directory_listing((0..self.devices.len()).map(|idx| idx.to_string())))
|
||||
}
|
||||
HandleKind::Device(idx) => self
|
||||
.devices
|
||||
.get(*idx)
|
||||
.map(format_device_info)
|
||||
.ok_or_else(|| Error::new(ENOENT)),
|
||||
HandleKind::Dev => {
|
||||
Ok(self.directory_listing(["input", "dri"].into_iter().map(String::from)))
|
||||
}
|
||||
HandleKind::DevInputDir => {
|
||||
let mut entries: Vec<String> = self
|
||||
.input_event_indices()
|
||||
.into_iter()
|
||||
.filter_map(|idx| basename(&self.devices[idx].devnode))
|
||||
.collect();
|
||||
if self.mouse_device_index().is_some() {
|
||||
entries.push("mice".to_string());
|
||||
}
|
||||
Ok(self.directory_listing(entries))
|
||||
}
|
||||
HandleKind::DevInput(idx) => {
|
||||
let dev = self.devices.get(*idx).ok_or_else(|| Error::new(ENOENT))?;
|
||||
if dev.scheme_target.is_empty() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let mut info = format_device_info(dev);
|
||||
info.push_str(&format!("SCHEME_TARGET={}\n", dev.scheme_target));
|
||||
Ok(info)
|
||||
}
|
||||
HandleKind::DevDri(idx) => {
|
||||
let dev = self.devices.get(*idx).ok_or_else(|| Error::new(ENOENT))?;
|
||||
if dev.scheme_target.is_empty() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let mut info = format_device_info(dev);
|
||||
info.push_str(&format!("SCHEME_TARGET={}\n", dev.scheme_target));
|
||||
Ok(info)
|
||||
}
|
||||
HandleKind::Link(idx) => {
|
||||
let dev = self.devices.get(*idx).ok_or_else(|| Error::new(ENOENT))?;
|
||||
if dev.scheme_target.is_empty() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let mut info = format_device_info(dev);
|
||||
info.push_str(&format!("SCHEME_TARGET={}\n", dev.scheme_target));
|
||||
Ok(info)
|
||||
}
|
||||
HandleKind::DevInputMice => {
|
||||
let idx = self
|
||||
.mouse_device_index()
|
||||
.ok_or_else(|| Error::new(ENOENT))?;
|
||||
let dev = &self.devices[idx];
|
||||
let mut info = format_device_info(dev);
|
||||
info.push_str(&format!("SCHEME_TARGET={}\n", dev.scheme_target));
|
||||
Ok(info)
|
||||
}
|
||||
HandleKind::DevDriDir => Ok(self.directory_listing(
|
||||
self.dri_card_indices()
|
||||
.into_iter()
|
||||
.filter_map(|idx| basename(&self.devices[idx].devnode)),
|
||||
)),
|
||||
HandleKind::DevLinks => {
|
||||
Ok(self.directory_listing(["input", "dri"].into_iter().map(String::from)))
|
||||
}
|
||||
HandleKind::LinksInputDir => {
|
||||
Ok(self.directory_listing(["by-path"].into_iter().map(String::from)))
|
||||
}
|
||||
HandleKind::LinksInputByPathDir => Ok(self.link_listing("/links/input/by-path/")),
|
||||
HandleKind::LinksDriDir => {
|
||||
Ok(self.directory_listing(["by-path"].into_iter().map(String::from)))
|
||||
}
|
||||
HandleKind::LinksDriByPathDir => Ok(self.link_listing("/links/dri/by-path/")),
|
||||
HandleKind::Uevent => Ok(self.uevent_content()),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_directory(kind: &HandleKind) -> bool {
|
||||
matches!(
|
||||
kind,
|
||||
HandleKind::Root
|
||||
| HandleKind::Devices
|
||||
| HandleKind::Dev
|
||||
| HandleKind::DevInputDir
|
||||
| HandleKind::DevDriDir
|
||||
| HandleKind::DevLinks
|
||||
| HandleKind::LinksInputDir
|
||||
| HandleKind::LinksInputByPathDir
|
||||
| HandleKind::LinksDriDir
|
||||
| HandleKind::LinksDriByPathDir
|
||||
)
|
||||
}
|
||||
|
||||
fn kind_for_id(&self, id: usize) -> Result<HandleKind> {
|
||||
if id == SCHEME_ROOT_ID {
|
||||
return Ok(HandleKind::Root);
|
||||
}
|
||||
|
||||
self.handles
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or_else(|| Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn kind_for_path(&self, path: &str) -> Result<HandleKind> {
|
||||
let cleaned = path.trim_matches('/');
|
||||
|
||||
match cleaned {
|
||||
"" => Ok(HandleKind::Root),
|
||||
"devices" => Ok(HandleKind::Devices),
|
||||
"dev" => Ok(HandleKind::Dev),
|
||||
"dev/input" => Ok(HandleKind::DevInputDir),
|
||||
"dev/input/mice" => {
|
||||
if self.mouse_device_index().is_none() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
Ok(HandleKind::DevInputMice)
|
||||
}
|
||||
"dev/dri" => Ok(HandleKind::DevDriDir),
|
||||
"links" => Ok(HandleKind::DevLinks),
|
||||
"links/input" => Ok(HandleKind::LinksInputDir),
|
||||
"links/input/by-path" => Ok(HandleKind::LinksInputByPathDir),
|
||||
"links/dri" => Ok(HandleKind::LinksDriDir),
|
||||
"links/dri/by-path" => Ok(HandleKind::LinksDriByPathDir),
|
||||
"uevent" => Ok(HandleKind::Uevent),
|
||||
_ => {
|
||||
if let Some(rest) = cleaned.strip_prefix("devices/") {
|
||||
let idx = rest.parse::<usize>().map_err(|_| Error::new(ENOENT))?;
|
||||
if idx >= self.devices.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
Ok(HandleKind::Device(idx))
|
||||
} else if let Some(rest) = cleaned.strip_prefix("dev/input/") {
|
||||
let devnode = format!("/dev/input/{rest}");
|
||||
let idx = self
|
||||
.find_device_by_devnode(&devnode)
|
||||
.ok_or_else(|| Error::new(ENOENT))?;
|
||||
Ok(HandleKind::DevInput(idx))
|
||||
} else if let Some(rest) = cleaned.strip_prefix("dev/dri/") {
|
||||
let devnode = format!("/dev/dri/{rest}");
|
||||
let idx = self
|
||||
.find_device_by_devnode(&devnode)
|
||||
.ok_or_else(|| Error::new(ENOENT))?;
|
||||
Ok(HandleKind::DevDri(idx))
|
||||
} else if let Some(rest) = cleaned.strip_prefix("links/input/by-path/") {
|
||||
let idx = self
|
||||
.find_device_by_link("/links/input/by-path/", rest)
|
||||
.ok_or_else(|| Error::new(ENOENT))?;
|
||||
Ok(HandleKind::Link(idx))
|
||||
} else if let Some(rest) = cleaned.strip_prefix("links/dri/by-path/") {
|
||||
let idx = self
|
||||
.find_device_by_link("/links/dri/by-path/", rest)
|
||||
.ok_or_else(|| Error::new(ENOENT))?;
|
||||
Ok(HandleKind::Link(idx))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn allocate_handle(&mut self, kind: HandleKind) -> usize {
|
||||
let id = self.next_id;
|
||||
self.next_id = self.next_id.saturating_add(1);
|
||||
self.handles.insert(id, kind);
|
||||
id
|
||||
}
|
||||
|
||||
fn path_for_handle(&self, kind: &HandleKind) -> Result<String> {
|
||||
match kind {
|
||||
HandleKind::Root => Ok("/scheme/udev".to_string()),
|
||||
HandleKind::Devices => Ok("/scheme/udev/devices".to_string()),
|
||||
HandleKind::Device(idx) => {
|
||||
if *idx >= self.devices.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
Ok(format!("/scheme/udev/devices/{idx}"))
|
||||
}
|
||||
HandleKind::Dev => Ok("/scheme/udev/dev".to_string()),
|
||||
HandleKind::DevInputDir => Ok("/scheme/udev/dev/input".to_string()),
|
||||
HandleKind::DevInput(idx) => self
|
||||
.devices
|
||||
.get(*idx)
|
||||
.filter(|dev| !dev.devnode.is_empty())
|
||||
.map(|dev| format!("/scheme/udev{}", dev.devnode))
|
||||
.ok_or_else(|| Error::new(ENOENT)),
|
||||
HandleKind::DevInputMice => Ok("/scheme/udev/dev/input/mice".to_string()),
|
||||
HandleKind::DevDriDir => Ok("/scheme/udev/dev/dri".to_string()),
|
||||
HandleKind::DevDri(idx) => self
|
||||
.devices
|
||||
.get(*idx)
|
||||
.filter(|dev| !dev.devnode.is_empty())
|
||||
.map(|dev| format!("/scheme/udev{}", dev.devnode))
|
||||
.ok_or_else(|| Error::new(ENOENT)),
|
||||
HandleKind::DevLinks => Ok("/scheme/udev/links".to_string()),
|
||||
HandleKind::LinksInputDir => Ok("/scheme/udev/links/input".to_string()),
|
||||
HandleKind::LinksInputByPathDir => Ok("/scheme/udev/links/input/by-path".to_string()),
|
||||
HandleKind::LinksDriDir => Ok("/scheme/udev/links/dri".to_string()),
|
||||
HandleKind::LinksDriByPathDir => Ok("/scheme/udev/links/dri/by-path".to_string()),
|
||||
HandleKind::Link(idx) => self
|
||||
.devices
|
||||
.get(*idx)
|
||||
.and_then(|dev| dev.symlinks.first())
|
||||
.map(|link| format!("/scheme/udev{link}"))
|
||||
.ok_or_else(|| Error::new(ENOENT)),
|
||||
HandleKind::Uevent => Ok("/scheme/udev/uevent".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl redox_scheme::SchemeBlockMut for UdevScheme {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
let cleaned = path.trim_matches('/');
|
||||
|
||||
let kind = if cleaned.is_empty() {
|
||||
HandleKind::Root
|
||||
} else if cleaned == "devices" || cleaned == "devices/" {
|
||||
HandleKind::Root
|
||||
} else if let Some(rest) = cleaned.strip_prefix("devices/") {
|
||||
let idx: usize = rest
|
||||
.trim_end_matches('/')
|
||||
.parse()
|
||||
.map_err(|_| Error::new(ENOENT))?;
|
||||
if idx >= self.devices.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
HandleKind::Device(idx)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
};
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle { kind, offset: 0 });
|
||||
Ok(Some(id))
|
||||
impl SchemeSync for UdevScheme {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
Ok(SCHEME_ROOT_ID)
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
if dirfd != SCHEME_ROOT_ID {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let content = match &handle.kind {
|
||||
HandleKind::Root => {
|
||||
let mut listing = String::new();
|
||||
for (i, dev) in self.devices.iter().enumerate() {
|
||||
listing.push_str(&format!("devices/{}\n", i));
|
||||
}
|
||||
listing
|
||||
}
|
||||
HandleKind::Device(idx) => {
|
||||
let dev = &self.devices[*idx];
|
||||
format_device_info(dev)
|
||||
}
|
||||
let kind = self.kind_for_path(path)?;
|
||||
let id = if matches!(kind, HandleKind::Root) {
|
||||
SCHEME_ROOT_ID
|
||||
} else {
|
||||
self.allocate_handle(kind)
|
||||
};
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let kind = self.kind_for_id(id)?;
|
||||
let content = self.content_for_handle(&kind)?;
|
||||
let bytes = content.as_bytes();
|
||||
let remaining = &bytes[handle.offset..];
|
||||
|
||||
if offset >= bytes.len() as u64 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let start = offset as usize;
|
||||
let remaining = &bytes[start..];
|
||||
let to_copy = remaining.len().min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&remaining[..to_copy]);
|
||||
handle.offset += to_copy;
|
||||
Ok(Some(to_copy))
|
||||
Ok(to_copy)
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, _buf: &[u8]) -> Result<Option<usize>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
_buf: &[u8],
|
||||
_offset: u64,
|
||||
_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
let _kind = self.kind_for_id(id)?;
|
||||
Err(Error::new(EROFS))
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let len = match &handle.kind {
|
||||
HandleKind::Root => self.devices.len() * 20,
|
||||
HandleKind::Device(idx) => format_device_info(&self.devices[*idx]).len(),
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
let kind = self.kind_for_id(id)?;
|
||||
let path = self.path_for_handle(&kind)?;
|
||||
let bytes = path.as_bytes();
|
||||
let to_copy = bytes.len().min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&bytes[..to_copy]);
|
||||
Ok(to_copy)
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> {
|
||||
let kind = self.kind_for_id(id)?;
|
||||
let size = self.content_for_handle(&kind)?.len() as u64;
|
||||
|
||||
stat.st_mode = if Self::is_directory(&kind) {
|
||||
MODE_DIR | 0o555
|
||||
} else {
|
||||
MODE_FILE | 0o444
|
||||
};
|
||||
let new_offset = match whence {
|
||||
SEEK_SET => pos as isize,
|
||||
SEEK_CUR => handle.offset as isize + pos,
|
||||
SEEK_END => len as isize + pos,
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
if new_offset < 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
stat.st_size = size;
|
||||
stat.st_blocks = size.div_ceil(512);
|
||||
stat.st_blksize = 4096;
|
||||
stat.st_nlink = 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
let _kind = self.kind_for_id(id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result<usize> {
|
||||
let _kind = self.kind_for_id(id)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result<u64> {
|
||||
let kind = self.kind_for_id(id)?;
|
||||
Ok(self.content_for_handle(&kind)?.len() as u64)
|
||||
}
|
||||
|
||||
fn ftruncate(&mut self, id: usize, _len: u64, _ctx: &CallerCtx) -> Result<()> {
|
||||
let _kind = self.kind_for_id(id)?;
|
||||
Err(Error::new(EROFS))
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result<EventFlags> {
|
||||
let _kind = self.kind_for_id(id)?;
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
if id != SCHEME_ROOT_ID {
|
||||
self.handles.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn path_exists(path: &str) -> bool {
|
||||
std::fs::metadata(path).is_ok()
|
||||
}
|
||||
|
||||
fn parse_pci_slot(name: &str) -> Option<(u8, u8, u8)> {
|
||||
let mut parts = name.split('.');
|
||||
let bus = parts.next()?.parse::<u8>().ok()?;
|
||||
let dev = parts.next()?.parse::<u8>().ok()?;
|
||||
let func = parts.next()?.parse::<u8>().ok()?;
|
||||
if parts.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
Some((bus, dev, func))
|
||||
}
|
||||
|
||||
fn basename(path: &str) -> Option<String> {
|
||||
path.rsplit('/').next().and_then(|part| {
|
||||
if part.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(part.to_string())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gpu_priority(dev: &DeviceInfo) -> u8 {
|
||||
match dev.vendor_id {
|
||||
0x1002 => 0,
|
||||
0x8086 => 1,
|
||||
_ => 2,
|
||||
}
|
||||
}
|
||||
|
||||
fn input_priority(dev: &DeviceInfo) -> u8 {
|
||||
if dev.is_input_keyboard() {
|
||||
0
|
||||
} else {
|
||||
match dev.input_kind {
|
||||
Some(InputKind::Mouse) => 1,
|
||||
Some(InputKind::Generic) | None => 2,
|
||||
Some(InputKind::Keyboard) => 0,
|
||||
}
|
||||
handle.offset = new_offset as usize;
|
||||
Ok(Some(new_offset))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
match &handle.kind {
|
||||
HandleKind::Root => {
|
||||
stat.st_mode = MODE_DIR | 0o555;
|
||||
}
|
||||
HandleKind::Device(_) => {
|
||||
stat.st_mode = MODE_FILE | 0o444;
|
||||
}
|
||||
}
|
||||
Ok(Some(0))
|
||||
}
|
||||
|
||||
fn fevent(&mut self, id: usize, _flags: EventFlags) -> Result<Option<EventFlags>> {
|
||||
let _ = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(EventFlags::empty()))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles.remove(&id);
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user