use asus_nb::aura_modes::{AuraModes, BREATHING, STATIC, STROBE}; use log::{info, warn}; use serde_derive::{Deserialize, Serialize}; use std::fs::OpenOptions; use std::io::Read; pub static LEDMODE_CONFIG_PATH: &str = "/etc/asusd/asusd-ledmodes.toml"; pub static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl"; pub struct LaptopBase { usb_product: String, condev_iface: Option, // required for finding the Consumer Device interface supported_modes: Vec, } impl LaptopBase { pub fn usb_product(&self) -> &str { &self.usb_product } pub fn condev_iface(&self) -> Option<&String> { self.condev_iface.as_ref() } pub fn supported_modes(&self) -> &[u8] { &self.supported_modes } } pub fn match_laptop() -> Option { for device in rusb::devices().unwrap().iter() { let device_desc = device.device_descriptor().unwrap(); if device_desc.vendor_id() == 0x0b05 { match device_desc.product_id() { 0x1866 => { let laptop = select_1866_device("1866".to_owned()); print_modes(&laptop.supported_modes); return Some(laptop); } 0x1869 => return Some(select_1866_device("1869".to_owned())), 0x1854 => { info!("Found GL753 or similar"); return Some(LaptopBase { usb_product: "1854".to_string(), condev_iface: None, supported_modes: vec![STATIC, BREATHING, STROBE], }); } _ => {} } } } warn!( "Unsupported laptop, please request support at {}", HELP_ADDRESS ); warn!("Continuing with minimal support"); None } fn select_1866_device(prod: String) -> LaptopBase { let dmi = sysfs_class::DmiId::default(); let board_name = dmi.board_name().expect("Could not get board_name"); let prod_family = dmi.product_family().expect("Could not get product_family"); let prod_name = dmi.product_name().expect("Could not get product_name"); info!("Product name: {}", prod_name.trim()); info!("Board name: {}", board_name.trim()); let mut laptop = LaptopBase { usb_product: prod, condev_iface: Some("02".to_owned()), supported_modes: vec![], }; if let Some(modes) = LEDModeGroup::load_from_config() { if let Some(led_modes) = modes.matcher(&prod_family, &board_name) { laptop.supported_modes = led_modes; return laptop; } } laptop } fn print_modes(supported_modes: &[u8]) { if !supported_modes.is_empty() { info!("Supported Keyboard LED modes are:"); for mode in supported_modes { let mode = <&str>::from(&::from(*mode)); info!("- {}", mode); } info!( "If these modes are incorrect or missing please request support at {}", HELP_ADDRESS ); } else { info!("No RGB control available"); } } #[derive(Debug, Deserialize, Serialize)] struct LEDModeGroup { led_modes: Vec, } impl LEDModeGroup { /// Consumes the LEDModes fn matcher(self, prod_family: &str, board_name: &str) -> Option> { for led_modes in self.led_modes { if prod_family.contains(&led_modes.prod_family) { for board in led_modes.board_names { if board_name.contains(&board) { info!("Matched to {} {}", led_modes.prod_family, board); return Some(led_modes.led_modes); } } } } None } fn load_from_config() -> Option { if let Ok(mut file) = OpenOptions::new().read(true).open(&LEDMODE_CONFIG_PATH) { let mut buf = String::new(); if let Ok(l) = file.read_to_string(&mut buf) { if l == 0 { warn!("{} is empty", LEDMODE_CONFIG_PATH); } else { return Some(toml::from_str(&buf).unwrap_or_else(|_| { panic!("Could not deserialise {}", LEDMODE_CONFIG_PATH) })); } } } warn!("Does {} exist?", LEDMODE_CONFIG_PATH); None } } #[derive(Debug, Deserialize, Serialize)] struct LEDModes { prod_family: String, board_names: Vec, led_modes: Vec, }