diff --git a/asusctl/src/aura_cli.rs b/asusctl/src/aura_cli.rs index c0326373..47138b6a 100644 --- a/asusctl/src/aura_cli.rs +++ b/asusctl/src/aura_cli.rs @@ -7,14 +7,14 @@ use std::str::FromStr; #[derive(Options)] pub struct LedBrightness { - level: Option, + level: Option, } impl LedBrightness { - pub fn new(level: Option) -> Self { + pub fn new(level: Option) -> Self { LedBrightness { level } } - pub fn level(&self) -> Option { + pub fn level(&self) -> Option { self.level } } diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index ec0c6ee6..a5dbba59 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -7,13 +7,7 @@ use daemon::{ }; use gumdrop::{Opt, Options}; use rog_dbus::AuraDbusClient; -use rog_types::{ - anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN}, - aura_modes::{AuraEffect, AuraModeNum}, - cli_options::{AniMeActions, AniMeStatusValue}, - gfx_vendors::GfxVendors, - profile::{FanLevel, ProfileCommand, ProfileEvent}, -}; +use rog_types::{anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN}, aura_modes::{self, AuraEffect, AuraModeNum}, cli_options::{AniMeActions, AniMeStatusValue}, gfx_vendors::GfxVendors, profile::{FanLevel, ProfileCommand, ProfileEvent}}; use std::env::args; use yansi_term::Colour::Green; use yansi_term::Colour::Red; @@ -202,7 +196,7 @@ fn main() -> Result<(), Box> { let level = dbus.proxies().led().get_led_brightness()?; println!("Current keyboard led brightness: {}", level.to_string()); } - Some(level) => dbus.proxies().led().set_led_brightness(level)?, + Some(level) => dbus.proxies().led().set_led_brightness(::from(level))?, } } diff --git a/daemon/src/config_aura.rs b/daemon/src/config_aura.rs index 55b6696b..2705bcd0 100644 --- a/daemon/src/config_aura.rs +++ b/daemon/src/config_aura.rs @@ -1,6 +1,6 @@ use crate::laptops::LaptopLedData; -use log::{error, warn}; -use rog_types::aura_modes::{AuraEffect, AuraModeNum, AuraMultiZone, AuraZone}; +use log::{error, info, warn}; +use rog_types::aura_modes::{AuraEffect, AuraModeNum, AuraMultiZone, AuraZone, LedBrightness}; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::fs::{File, OpenOptions}; @@ -8,9 +8,28 @@ use std::io::{Read, Write}; pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf"; +#[derive(Deserialize, Serialize)] +pub struct AuraConfigV320 { + pub brightness: u32, + pub current_mode: AuraModeNum, + pub builtins: BTreeMap, + pub multizone: Option, +} + +impl AuraConfigV320 { + pub(crate) fn into_current(self) -> AuraConfig { + AuraConfig { + brightness: ::from(self.brightness), + current_mode: self.current_mode, + builtins: self.builtins, + multizone: self.multizone, + } + } +} + #[derive(Deserialize, Serialize)] pub struct AuraConfig { - pub brightness: u8, + pub brightness: LedBrightness, pub current_mode: AuraModeNum, pub builtins: BTreeMap, pub multizone: Option, @@ -19,7 +38,7 @@ pub struct AuraConfig { impl Default for AuraConfig { fn default() -> Self { AuraConfig { - brightness: 1, + brightness: LedBrightness::Med, current_mode: AuraModeNum::Static, builtins: BTreeMap::new(), multizone: None, @@ -48,6 +67,11 @@ impl AuraConfig { } else { if let Ok(data) = serde_json::from_str(&buf) { return data; + } else if let Ok(data) = serde_json::from_str::(&buf) { + let config = data.into_current(); + config.write(); + info!("Updated AuraConfig version"); + return config; } warn!("Could not deserialise {}", AURA_CONFIG_PATH); panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH); diff --git a/daemon/src/ctrl_leds.rs b/daemon/src/ctrl_leds.rs index c5531974..867ae00f 100644 --- a/daemon/src/ctrl_leds.rs +++ b/daemon/src/ctrl_leds.rs @@ -7,13 +7,10 @@ static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness"; use crate::{ config_aura::AuraConfig, error::RogError, - laptops::{match_laptop, LaptopLedData, HELP_ADDRESS}, + laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, }; use log::{error, info, warn}; -use rog_types::{ - aura_modes::{AuraEffect, AuraModeNum}, - LED_MSG_LEN, -}; +use rog_types::{LED_MSG_LEN, aura_modes::{AuraEffect, AuraModeNum, LedBrightness}}; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::Path; @@ -37,19 +34,21 @@ impl GetSupported for CtrlKbdBacklight { fn get_supported() -> Self::A { // let mode = <&str>::from(&::from(*mode)); - let mut stock_led_modes = None; let multizone_led_mode = false; let per_key_led_mode = false; - if let Some(laptop) = match_laptop() { - stock_led_modes = if laptop.supported_modes().standard.is_empty() { + let laptop = LaptopLedData::get_data(); + let stock_led_modes = if let Some(data) = laptop { + if data.standard.is_empty() { None } else { - Some(laptop.supported_modes().standard.clone()) - }; - } + Some(data.standard) + } + } else { + None + }; LedSupportedFunctions { - brightness_set: CtrlKbdBacklight::get_kbd_bright_path().is_ok(), + brightness_set: CtrlKbdBacklight::get_kbd_bright_path().is_some(), stock_led_modes, multizone_led_mode, per_key_led_mode, @@ -59,8 +58,6 @@ impl GetSupported for CtrlKbdBacklight { pub struct CtrlKbdBacklight { led_node: Option, - #[allow(dead_code)] - kbd_node: Option, pub bright_node: String, supported_modes: LaptopLedData, flip_effect_write: bool, @@ -99,7 +96,7 @@ impl crate::ZbusAdd for DbusKbdBacklight { /// LED commands are split between Brightness, Modes, Per-Key #[dbus_interface(name = "org.asuslinux.Daemon")] impl DbusKbdBacklight { - fn set_brightness(&mut self, brightness: u8) { + fn set_brightness(&mut self, brightness: LedBrightness) { if let Ok(ctrl) = self.inner.try_lock() { ctrl.set_brightness(brightness) .map_err(|err| warn!("{}", err)) @@ -245,9 +242,9 @@ impl crate::CtrlTask for CtrlKbdBacklight { file.read_exact(&mut buf) .map_err(|err| RogError::Read("buffer".into(), err))?; if let Some(num) = char::from(buf[0]).to_digit(10) { - if self.config.brightness != num as u8 { + if self.config.brightness != num.into() { self.config.read(); - self.config.brightness = num as u8; + self.config.brightness = num.into(); self.config.write(); } return Ok(()); @@ -258,46 +255,37 @@ impl crate::CtrlTask for CtrlKbdBacklight { impl CtrlKbdBacklight { #[inline] - pub fn new( - id_product: &str, - condev_iface: Option<&String>, - supported_modes: LaptopLedData, - config: AuraConfig, - ) -> Result { + pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result { // TODO: return error if *all* nodes are None - let led_node = Self::get_node_failover(id_product, None, Self::scan_led_node).map_or_else( - |err| { - warn!("led_node: {}", err); - None - }, - Some, - ); - - let kbd_node = Self::get_node_failover(id_product, condev_iface, Self::scan_kbd_node) - .map_or_else( - |err| { - warn!("kbd_node: {}", err); - None - }, - Some, - ); + let mut led_node = None; + for prod in ASUS_KEYBOARD_DEVICES.iter() { + match Self::find_led_node(prod) { + Ok(node) => { + led_node = Some(node); + break; + } + Err(err) => warn!("led_node: {}", err), + } + } let bright_node = Self::get_kbd_bright_path(); - if led_node.is_none() && kbd_node.is_none() && Self::get_kbd_bright_path().is_err() { + if led_node.is_none() && bright_node.is_none() { return Err(RogError::MissingFunction( "All keyboard features missing, you may require a v5.11 series kernel or newer" .into(), )); } + if bright_node.is_none() { + return Err(RogError::MissingFunction( + "No brightness control, you may require a v5.11 series kernel or newer".into(), + )); + } + let ctrl = CtrlKbdBacklight { - // Using `ok` here so we can continue without keyboard features but - // still get brightness control at least... maybe... led_node, - kbd_node, - // TODO: Check for existance - bright_node: bright_node?.to_owned(), + bright_node: bright_node.unwrap(), // If was none then we already returned above supported_modes, flip_effect_write: false, config, @@ -305,14 +293,11 @@ impl CtrlKbdBacklight { Ok(ctrl) } - fn get_kbd_bright_path() -> Result<&'static str, RogError> { + fn get_kbd_bright_path() -> Option { if Path::new(KBD_BRIGHT_PATH).exists() { - Ok(KBD_BRIGHT_PATH) - } else { - Err(RogError::MissingFunction( - "Keyboard features missing, you may require a v5.11 series kernel or newer".into(), - )) + return Some(KBD_BRIGHT_PATH.to_string()); } + None } pub fn get_brightness(&self) -> Result { @@ -331,36 +316,23 @@ impl CtrlKbdBacklight { Ok(buf[0]) } - pub fn set_brightness(&self, brightness: u8) -> Result<(), RogError> { + pub fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> { + let path = Path::new(&self.bright_node); let mut file = OpenOptions::new() .write(true) - .open(&self.bright_node) + .open(&path) .map_err(|err| match err.kind() { std::io::ErrorKind::NotFound => { RogError::MissingLedBrightNode((&self.bright_node).into(), err) } _ => RogError::Path((&self.bright_node).into(), err), })?; - file.write_all(&[brightness]) + file.write_all(&[brightness.as_char_code()]) .map_err(|err| RogError::Read("buffer".into(), err))?; Ok(()) } - fn get_node_failover( - id_product: &str, - iface: Option<&String>, - fun: fn(&str, Option<&String>) -> Result, - ) -> Result { - match fun(id_product, iface) { - Ok(o) => return Ok(o), - Err(e) => { - warn!("Looking for node: {}", e.to_string()); - } - } - Err(RogError::NotFound(format!("{}, {:?}", id_product, iface))) - } - - fn scan_led_node(id_product: &str, _: Option<&String>) -> Result { + fn find_led_node(id_product: &str) -> Result { let mut enumerator = udev::Enumerator::new().map_err(|err| { warn!("{}", err); RogError::Udev("enumerator failed".into(), err) @@ -393,57 +365,11 @@ impl CtrlKbdBacklight { } } } - warn!("Did not find a hidraw node for LED control, your device may be unsupported or require a kernel patch, see: {}", HELP_ADDRESS); Err(RogError::MissingFunction( "ASUS LED device node not found".into(), )) } - fn scan_kbd_node(id_product: &str, iface: Option<&String>) -> Result { - let mut enumerator = udev::Enumerator::new().map_err(|err| { - warn!("{}", err); - RogError::Udev("enumerator failed".into(), err) - })?; - enumerator.match_subsystem("input").map_err(|err| { - warn!("{}", err); - RogError::Udev("match_subsystem failed".into(), err) - })?; - enumerator - .match_property("ID_MODEL_ID", id_product) - .map_err(|err| { - warn!("{}", err); - RogError::Udev("match_property failed".into(), err) - })?; - - for device in enumerator - .scan_devices() - .map_err(|err| { - warn!("{}", err); - err - }) - .map_err(|err| { - warn!("{}", err); - RogError::Udev("scan_devices failed".into(), err) - })? - { - if let Some(dev_node) = device.devnode() { - if let Some(inum) = device.property_value("ID_USB_INTERFACE_NUM") { - if let Some(iface) = iface { - if inum == iface.as_str() { - info!("Using device at: {:?} for keyboard polling", dev_node); - return Ok(dev_node.to_string_lossy().to_string()); - } - } - } - } - } - - warn!("Did not find keyboard consumer device node, if expected functions are missing please file an issue at {}", HELP_ADDRESS); - Err(RogError::MissingFunction( - "ASUS keyboard 'Consumer Device' node not found".into(), - )) - } - pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> { self.set_and_save(mode) } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index 10accc2d..ac5a22a3 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -1,6 +1,5 @@ -use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu}; +use daemon::{ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu}, laptops::LaptopLedData}; use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight}; -use daemon::laptops::match_laptop; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; @@ -136,13 +135,12 @@ fn start_daemon() -> Result<(), Box> { DbusFanAndCpu::new(tmp).add_to_server(&mut object_server); }; - if let Some(laptop) = match_laptop() { - let aura_config = AuraConfig::load(laptop.supported_modes()); + if let Some(laptop) = LaptopLedData::get_data() { + if !laptop.standard.is_empty() { + let aura_config = AuraConfig::load(&laptop); if let Ok(ctrl) = CtrlKbdBacklight::new( - laptop.usb_product(), - laptop.condev_iface(), - laptop.supported_modes().to_owned(), + laptop, aura_config, ) .map_err(|err| { @@ -153,7 +151,7 @@ fn start_daemon() -> Result<(), Box> { DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server); tasks.push(tmp); } - } + }} // TODO: implement messaging between threads to check fails // These tasks generally read a sys path or file to check for a diff --git a/daemon/src/laptops.rs b/daemon/src/laptops.rs index 366f4d0a..b8929a53 100644 --- a/daemon/src/laptops.rs +++ b/daemon/src/laptops.rs @@ -4,80 +4,8 @@ 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"; -static LAPTOP_DEVICES: [u16; 4] = [0x1866, 0x1869, 0x1854, 0x19b6]; - -/// A helper of sorts specifically for functions tied to laptop models -#[derive(Debug)] -pub struct LaptopBase { - usb_product: String, - condev_iface: Option, // required for finding the Consumer Device interface - led_support: LaptopLedData, -} - -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) -> &LaptopLedData { - &self.led_support - } -} - -pub fn match_laptop() -> Option { - for device in rusb::devices().expect("Couldn't get device").iter() { - let device_desc = device - .device_descriptor() - .expect("Couldn't get device descriptor"); - if device_desc.vendor_id() == 0x0b05 && LAPTOP_DEVICES.contains(&device_desc.product_id()) { - let prod_str = format!("{:x?}", device_desc.product_id()); - - if device_desc.product_id() == 0x1854 { - let laptop = laptop(prod_str, None); - return Some(laptop); - } - - let laptop = laptop(prod_str, Some("02".to_owned())); - return Some(laptop); - } - } - warn!( - "Unsupported laptop, please request support at {}", - HELP_ADDRESS - ); - warn!("Continuing with minimal support"); - None -} - -fn laptop(prod: String, condev_iface: Option) -> 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 mut laptop = LaptopBase { - usb_product: prod, - condev_iface, - led_support: LaptopLedData { - board_names: vec![], - prod_family: String::new(), - standard: vec![], - multizone: false, - per_key: false, - }, - }; - - if let Some(modes) = LedSupportFile::load_from_config() { - if let Some(led_modes) = modes.matcher(&prod_family, &board_name) { - laptop.led_support = led_modes; - return laptop; - } - } - laptop -} +pub const ASUS_LED_MODE_CONF: &str = "/etc/asusd/asusd-ledmodes.toml"; +pub const ASUS_KEYBOARD_DEVICES: [&str; 4] = ["1866", "1869", "1854", "19b6"]; pub fn print_board_info() { let dmi = sysfs_class::DmiId::default(); @@ -98,8 +26,8 @@ pub fn print_modes(supported_modes: &[u8]) { info!("- {}", mode); } info!( - "If these modes are incorrect or missing please request support at {}", - HELP_ADDRESS + "If these modes are incorrect you can edit {}", + ASUS_LED_MODE_CONF ); } else { info!("No RGB control available"); @@ -120,6 +48,19 @@ pub struct LaptopLedData { pub per_key: bool, } +impl LaptopLedData { + pub fn get_data() -> Option { + 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"); + + if let Some(modes) = LedSupportFile::load_from_config() { + return modes.matcher(&prod_family, &board_name); + } + None + } +} + impl LedSupportFile { /// Consumes the LEDModes fn matcher(self, prod_family: &str, board_name: &str) -> Option { @@ -137,19 +78,19 @@ impl LedSupportFile { } fn load_from_config() -> Option { - if let Ok(mut file) = OpenOptions::new().read(true).open(&LEDMODE_CONFIG_PATH) { + if let Ok(mut file) = OpenOptions::new().read(true).open(&ASUS_LED_MODE_CONF) { let mut buf = String::new(); if let Ok(l) = file.read_to_string(&mut buf) { if l == 0 { - warn!("{} is empty", LEDMODE_CONFIG_PATH); + warn!("{} is empty", ASUS_LED_MODE_CONF); } else { return Some(toml::from_str(&buf).unwrap_or_else(|_| { - panic!("Could not deserialise {}", LEDMODE_CONFIG_PATH) + panic!("Could not deserialise {}", ASUS_LED_MODE_CONF) })); } } } - warn!("Does {} exist?", LEDMODE_CONFIG_PATH); + warn!("Does {} exist?", ASUS_LED_MODE_CONF); None } } diff --git a/rog-dbus/src/zbus_led.rs b/rog-dbus/src/zbus_led.rs index cd016531..37b3c641 100644 --- a/rog-dbus/src/zbus_led.rs +++ b/rog-dbus/src/zbus_led.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, Mutex}; use zbus::{dbus_proxy, Connection, Result}; -use rog_types::{aura_modes::AuraEffect, aura_perkey::KeyColourArray}; +use rog_types::{aura_modes::{AuraEffect, LedBrightness}, aura_perkey::KeyColourArray}; const BLOCKING_TIME: u64 = 40; // 100ms = 10 FPS, max 50ms = 20 FPS, 40ms = 25 FPS @@ -39,7 +39,7 @@ trait Daemon { fn prev_led_mode(&self) -> zbus::Result<()>; /// SetBrightness method - fn set_brightness(&self, brightness: u8) -> zbus::Result<()>; + fn set_brightness(&self, brightness: LedBrightness) -> zbus::Result<()>; /// SetLedMode method fn set_led_mode(&self, effect: &AuraEffect) -> zbus::Result<()>; @@ -80,7 +80,7 @@ impl<'a> LedProxy<'a> { } #[inline] - pub fn set_led_brightness(&self, level: u8) -> Result<()> { + pub fn set_led_brightness(&self, level: LedBrightness) -> Result<()> { self.0.set_brightness(level)?; Ok(()) } diff --git a/rog-types/src/aura_modes.rs b/rog-types/src/aura_modes.rs index 42ed78da..b15ed514 100644 --- a/rog-types/src/aura_modes.rs +++ b/rog-types/src/aura_modes.rs @@ -10,6 +10,32 @@ use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; use zvariant_derive::Type; +#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize, Type)] +pub enum LedBrightness { + Off, + Low, + Med, + High, +} + +impl LedBrightness { + pub fn as_char_code(&self) -> u8 { + std::char::from_digit(*self as u32, 10).unwrap() as u8 + } +} + +impl From for LedBrightness { + fn from(bright: u32) -> Self { + match bright { + 0 => LedBrightness::Off, + 1 => LedBrightness::Low, + 2 => LedBrightness::Med, + 3 => LedBrightness::High, + _ => LedBrightness::Med, + } + } +} + #[derive(Debug, Clone, PartialEq, Copy, Deserialize, Serialize, Type)] pub struct Colour(pub u8, pub u8, pub u8);