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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user