diff --git a/.gitignore b/.gitignore index bec5eaa6..06c82ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ cargo-config vendor-* vendor_* .vscode-ctags +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6c65c5ca..7a38b067 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,6 +443,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "concat-idents" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6f90860248d75014b7b103db8fee4f291c07bfb41306cdf77a0a5ab7a10d2f" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "concurrent-queue" version = "1.2.4" @@ -1203,6 +1213,28 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "inotify" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf888f9575c290197b2c948dc9e9ff10bd1a39ad1ea8585f734585fa6b9d3f9" +dependencies = [ + "bitflags", + "futures-core", + "inotify-sys", + "libc", + "tokio", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -2017,6 +2049,8 @@ dependencies = [ name = "rog_platform" version = "0.1.0" dependencies = [ + "concat-idents", + "inotify", "log", "rog_aura", "rusb", @@ -2408,6 +2442,20 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "libc", + "mio", + "pin-project-lite", + "socket2", + "winapi", +] + [[package]] name = "toml" version = "0.5.9" diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs index 1489d60c..40b23e34 100644 --- a/daemon/src/ctrl_aura/config.rs +++ b/daemon/src/ctrl_aura/config.rs @@ -135,7 +135,7 @@ impl Default for AuraConfig { } if let Ok(p) = KeyboardLed::new() { - if p.has_keyboard_rgb_mode() { + if p.has_kbd_rgb_mode() { prod_id = AuraDevice::Tuf; } } diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs index 9cdadb04..5ce525d3 100644 --- a/daemon/src/ctrl_aura/controller.rs +++ b/daemon/src/ctrl_aura/controller.rs @@ -42,7 +42,7 @@ impl GetSupported for CtrlKbdLed { } if let Ok(p) = KeyboardLed::new() { - if p.has_keyboard_rgb_mode() { + if p.has_kbd_rgb_mode() { prod_id = AuraDevice::Tuf; } } @@ -199,7 +199,7 @@ impl CtrlKbdLed { let bright_node = KeyboardLed::new(); let platform = KeyboardLed::new()?; - if led_node.is_none() && !platform.has_keyboard_rgb_mode() { + if led_node.is_none() && !platform.has_kbd_rgb_mode() { let dmi = sysfs_class::DmiId::default(); if let Ok(prod_family) = dmi.product_family() { if prod_family.contains("TUF") { @@ -212,7 +212,7 @@ impl CtrlKbdLed { let led_node = if let Some(rog) = led_node { info!("Found ROG USB keyboard"); LEDNode::Rog(rog) - } else if platform.has_keyboard_rgb_mode() { + } else if platform.has_kbd_rgb_mode() { info!("Found TUF keyboard"); LEDNode::KbdLed(platform) } else { @@ -269,7 +269,7 @@ impl CtrlKbdLed { if let LEDNode::KbdLed(platform) = &mut self.led_node { if let Some(pwr) = AuraPowerConfig::to_tuf_bool_array(&self.config.enabled) { let buf = [1, pwr[1] as u8, pwr[2] as u8, pwr[3] as u8, pwr[4] as u8]; - platform.set_keyboard_rgb_state(&buf)?; + platform.set_kbd_rgb_state(&buf)?; } } else if let LEDNode::Rog(hid_raw) = &self.led_node { let bytes = AuraPowerConfig::to_bytes(&self.config.enabled); @@ -365,7 +365,7 @@ impl CtrlKbdLed { mode.colour1.2, mode.speed as u8, ]; - platform.set_keyboard_rgb_mode(&buf)?; + platform.set_kbd_rgb_mode(&buf)?; } else if let LEDNode::Rog(hid_raw) = &self.led_node { let bytes: [u8; LED_MSG_LEN] = mode.into(); hid_raw.write_bytes(&bytes)?; diff --git a/daemon/src/ctrl_platform.rs b/daemon/src/ctrl_platform.rs index 19fc0a31..2691e4cb 100644 --- a/daemon/src/ctrl_platform.rs +++ b/daemon/src/ctrl_platform.rs @@ -156,6 +156,7 @@ impl crate::Reloadable for CtrlRogBios { impl CtrlRogBios { pub fn new(config: Arc>) -> Result { let platform = AsusPlatform::new()?; + if !platform.has_gpu_mux_mode() { info!("G-Sync Switchable Graphics not detected"); info!("If your laptop is not a G-Sync enabled laptop then you can ignore this. Standard graphics switching will still work."); diff --git a/daemon/src/ctrl_charge.rs b/daemon/src/ctrl_power.rs similarity index 58% rename from daemon/src/ctrl_charge.rs rename to daemon/src/ctrl_power.rs index aacd4a98..4b39ea0a 100644 --- a/daemon/src/ctrl_charge.rs +++ b/daemon/src/ctrl_power.rs @@ -3,38 +3,38 @@ use crate::{config::Config, error::RogError, GetSupported}; use async_trait::async_trait; use log::{info, warn}; use logind_zbus::manager::ManagerProxy; +use rog_platform::power::AsusPower; use rog_platform::supported::ChargeSupportedFunctions; use smol::stream::StreamExt; use smol::Executor; -use std::fs::OpenOptions; -use std::io::Write; -use std::path::Path; use std::sync::Arc; use std::sync::Mutex; use zbus::dbus_interface; use zbus::Connection; use zbus::SignalContext; -static BAT_CHARGE_PATH0: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold"; -static BAT_CHARGE_PATH1: &str = "/sys/class/power_supply/BAT1/charge_control_end_threshold"; -static BAT_CHARGE_PATH2: &str = "/sys/class/power_supply/BAT2/charge_control_end_threshold"; - -impl GetSupported for CtrlCharge { +impl GetSupported for CtrlPower { type A = ChargeSupportedFunctions; fn get_supported() -> Self::A { ChargeSupportedFunctions { - charge_level_set: CtrlCharge::get_battery_path().is_ok(), + charge_level_set: if let Ok(power) = AsusPower::new() { + power.has_charge_control_end_threshold() + } else { + false + }, } } } -pub struct CtrlCharge { +#[derive(Clone)] +pub struct CtrlPower { + power: AsusPower, config: Arc>, } #[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlCharge { +impl CtrlPower { async fn set_limit( &mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>, @@ -43,14 +43,12 @@ impl CtrlCharge { if !(20..=100).contains(&limit) { return Err(RogError::ChargeLimit(limit))?; } - if let Ok(mut config) = self.config.try_lock() { - Self::set(limit, &mut config) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - } + self.set(limit) + .map_err(|err| { + warn!("CtrlCharge: set_limit {}", err); + err + }) + .ok(); Self::notify_charge(&ctxt, limit).await?; Ok(()) } @@ -67,68 +65,51 @@ impl CtrlCharge { } #[async_trait] -impl crate::ZbusAdd for CtrlCharge { +impl crate::ZbusAdd for CtrlPower { async fn add_to_server(self, server: &mut Connection) { Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await; } } -impl crate::Reloadable for CtrlCharge { +impl crate::Reloadable for CtrlPower { fn reload(&mut self) -> Result<(), RogError> { if let Ok(mut config) = self.config.try_lock() { config.read(); - Self::set(config.bat_charge_limit, &mut config)?; + self.set(config.bat_charge_limit)?; } Ok(()) } } -impl CtrlCharge { +impl CtrlPower { pub fn new(config: Arc>) -> Result { - CtrlCharge::get_battery_path()?; - Ok(CtrlCharge { config }) + Ok(CtrlPower { + power: AsusPower::new()?, + config, + }) } - fn get_battery_path() -> Result<&'static str, RogError> { - if Path::new(BAT_CHARGE_PATH0).exists() { - Ok(BAT_CHARGE_PATH0) - } else if Path::new(BAT_CHARGE_PATH1).exists() { - Ok(BAT_CHARGE_PATH1) - } else if Path::new(BAT_CHARGE_PATH2).exists() { - Ok(BAT_CHARGE_PATH2) - } else { - Err(RogError::MissingFunction( - "Charge control not available, you may require a v5.8.10 series kernel or newer" - .into(), - )) - } - } - - pub(super) fn set(limit: u8, config: &mut Config) -> Result<(), RogError> { + pub(super) fn set(&self, limit: u8) -> Result<(), RogError> { if !(20..=100).contains(&limit) { return Err(RogError::ChargeLimit(limit)); } - let path = Self::get_battery_path()?; + self.power.set_charge_control_end_threshold(limit)?; - let mut file = OpenOptions::new() - .write(true) - .open(path) - .map_err(|err| RogError::Path(path.into(), err))?; - file.write_all(limit.to_string().as_bytes()) - .map_err(|err| RogError::Write(path.into(), err))?; info!("Battery charge limit: {}", limit); - config.read(); - config.bat_charge_limit = limit; - config.write(); + if let Ok(mut config) = self.config.try_lock() { + config.read(); + config.bat_charge_limit = limit; + config.write(); + } Ok(()) } } #[async_trait] -impl CtrlTask for CtrlCharge { +impl CtrlTask for CtrlPower { async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { let connection = Connection::system() .await @@ -138,7 +119,7 @@ impl CtrlTask for CtrlCharge { .await .expect("CtrlCharge could not create ManagerProxy"); - let config1 = self.config.clone(); + let power = self.clone(); executor .spawn(async move { if let Ok(notif) = manager.receive_prepare_for_sleep().await { @@ -148,8 +129,9 @@ impl CtrlTask for CtrlCharge { // If waking up if !args.start { info!("CtrlCharge reloading charge limit"); - if let Ok(mut lock) = config1.try_lock() { - Self::set(lock.bat_charge_limit, &mut lock) + if let Ok(lock) = power.config.try_lock() { + power + .set(lock.bat_charge_limit) .map_err(|err| { warn!("CtrlCharge: set_limit {}", err); err @@ -168,7 +150,7 @@ impl CtrlTask for CtrlCharge { .await .expect("CtrlCharge could not create ManagerProxy"); - let config = self.config.clone(); + let power = self.clone(); executor .spawn(async move { if let Ok(notif) = manager.receive_prepare_for_shutdown().await { @@ -178,16 +160,14 @@ impl CtrlTask for CtrlCharge { // If waking up - intention is to catch hibernation event if !args.start { info!("CtrlCharge reloading charge limit"); - loop { - if let Ok(mut lock) = config.clone().try_lock() { - Self::set(lock.bat_charge_limit, &mut lock) - .map_err(|err| { - warn!("CtrlCharge: set_limit {}", err); - err - }) - .ok(); - break; - } + if let Ok(lock) = power.config.try_lock() { + power + .set(lock.bat_charge_limit) + .map_err(|err| { + warn!("CtrlCharge: set_limit {}", err); + err + }) + .ok(); } } } diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index ff6a3414..4c4bec3d 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -5,8 +5,8 @@ use zbus::Connection; use zvariant::Type; use crate::{ - ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_charge::CtrlCharge, - ctrl_platform::CtrlRogBios, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported, + ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlRogBios, + ctrl_power::CtrlPower, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported, }; use rog_platform::supported::*; @@ -41,7 +41,7 @@ impl GetSupported for SupportedFunctions { SupportedFunctions { anime_ctrl: CtrlAnime::get_supported(), keyboard_led: CtrlKbdLed::get_supported(), - charge_ctrl: CtrlCharge::get_supported(), + charge_ctrl: CtrlPower::get_supported(), platform_profile: CtrlPlatformProfile::get_supported(), rog_bios_ctrl: CtrlRogBios::get_supported(), } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index cd637420..01169425 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -16,8 +16,8 @@ use daemon::ctrl_aura::config::AuraConfig; use daemon::ctrl_aura::controller::{ CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus, }; -use daemon::ctrl_charge::CtrlCharge; use daemon::ctrl_platform::CtrlRogBios; +use daemon::ctrl_power::CtrlPower; use daemon::ctrl_profiles::config::ProfileConfig; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, @@ -94,7 +94,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> } } - match CtrlCharge::new(config.clone()) { + match CtrlPower::new(config.clone()) { Ok(mut ctrl) => { // Do a reload of any settings ctrl.reload() @@ -102,7 +102,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> // Then register to dbus server ctrl.add_to_server(&mut connection).await; - let task = CtrlCharge::new(config)?; + let task = CtrlPower::new(config)?; task.create_tasks(executor).await.ok(); } Err(err) => { diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 7d52a021..6f297277 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -5,10 +5,10 @@ pub mod config; pub mod ctrl_anime; /// Keyboard LED brightness control, RGB, and LED display modes pub mod ctrl_aura; -/// Control of battery charge level -pub mod ctrl_charge; /// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode pub mod ctrl_platform; +/// Control of battery charge level +pub mod ctrl_power; /// Control CPU min/max freq and turbo, fan mode, fan curves /// /// Intel machines can control: diff --git a/rog-platform/Cargo.toml b/rog-platform/Cargo.toml index 46d279a1..23e9a801 100644 --- a/rog-platform/Cargo.toml +++ b/rog-platform/Cargo.toml @@ -16,4 +16,7 @@ zvariant_derive = "^3.0" udev = "^0.6" rusb = "^0.9" -sysfs-class = "^0.1" \ No newline at end of file +sysfs-class = "^0.1" +inotify = "^0.10.0" + +concat-idents = "1.1.3" \ No newline at end of file diff --git a/rog-platform/src/hid_raw.rs b/rog-platform/src/hid_raw.rs index 916f7985..d332bb8a 100644 --- a/rog-platform/src/hid_raw.rs +++ b/rog-platform/src/hid_raw.rs @@ -4,7 +4,7 @@ use log::{info, warn}; use crate::error::{PlatformError, Result}; -#[derive(Debug, PartialEq, PartialOrd)] +#[derive(Debug, PartialEq, PartialOrd, Clone)] pub struct HidRaw(PathBuf); impl HidRaw { diff --git a/rog-platform/src/keyboard_led.rs b/rog-platform/src/keyboard_led.rs index 2aed4a12..0d0007b1 100644 --- a/rog-platform/src/keyboard_led.rs +++ b/rog-platform/src/keyboard_led.rs @@ -3,13 +3,15 @@ use std::path::PathBuf; use log::warn; use crate::{ - attr_u8, attr_u8_array, + attr_u8, error::{PlatformError, Result}, - to_device, + has_attr, set_attr_u8_array, to_device, }; -#[derive(Debug, Default, PartialEq, PartialOrd)] -pub struct KeyboardLed(PathBuf); +#[derive(Debug, Default, PartialEq, PartialOrd, Clone)] +pub struct KeyboardLed { + path: PathBuf, +} impl KeyboardLed { pub fn new() -> Result { @@ -34,26 +36,28 @@ impl KeyboardLed { warn!("{}", err); PlatformError::Udev("scan_devices failed".into(), err) })? { - return Ok(Self(device.syspath().to_owned())); + return Ok(Self { + path: device.syspath().to_owned(), + }); } Err(PlatformError::MissingFunction( "asus::kbd_backlight not found".into(), )) } - attr_u8!(has_brightness, get_brightness, set_brightness, "brightness"); + attr_u8!("brightness", path); - attr_u8_array!( - has_keyboard_rgb_mode, - get_keyboard_rgb_mode, - set_keyboard_rgb_mode, + has_attr!("kbd_rgb_mode" path); + set_attr_u8_array!( + /// kbd_rgb_mode can only be set, not read back "kbd_rgb_mode" + path ); - attr_u8_array!( - has_keyboard_rgb_state, - get_keyboard_rgb_state, - set_keyboard_rgb_state, + has_attr!("kbd_rgb_state" path); + set_attr_u8_array!( + /// kbd_rgb_state can only be set, not read back "kbd_rgb_state" + path ); } diff --git a/rog-platform/src/lib.rs b/rog-platform/src/lib.rs index 1bc6a642..88962bc8 100644 --- a/rog-platform/src/lib.rs +++ b/rog-platform/src/lib.rs @@ -4,7 +4,9 @@ pub mod error; pub mod hid_raw; pub mod keyboard_led; +pub(crate) mod macros; pub mod platform; +pub mod power; pub mod supported; pub mod usb_raw; @@ -15,66 +17,6 @@ use udev::Device; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -#[macro_export] -macro_rules! attr_bool { - ($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => { - pub fn $hasser(&self) -> bool { - match to_device(&self.0) { - Ok(p) => crate::has_attr(&p, $attr_name), - Err(_) => false, - } - } - - pub fn $getter(&self) -> Result { - crate::read_attr_bool(&to_device(&self.0)?, $attr_name) - } - - pub fn $setter(&self, value: bool) -> Result<()> { - crate::write_attr_bool(&mut to_device(&self.0)?, $attr_name, value) - } - }; -} - -#[macro_export] -macro_rules! attr_u8 { - ($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => { - pub fn $hasser(&self) -> bool { - match to_device(&self.0) { - Ok(p) => crate::has_attr(&p, $attr_name), - Err(_) => false, - } - } - - pub fn $getter(&self) -> Result { - crate::read_attr_u8(&to_device(&self.0)?, $attr_name) - } - - pub fn $setter(&self, value: u8) -> Result<()> { - crate::write_attr_u8(&mut to_device(&self.0)?, $attr_name, value) - } - }; -} - -#[macro_export] -macro_rules! attr_u8_array { - ($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => { - pub fn $hasser(&self) -> bool { - match to_device(&self.0) { - Ok(p) => crate::has_attr(&p, $attr_name), - Err(_) => false, - } - } - - pub fn $getter(&self) -> Result> { - crate::read_attr_u8_array(&to_device(&self.0)?, $attr_name) - } - - pub fn $setter(&self, values: &[u8]) -> Result<()> { - crate::write_attr_u8_array(&mut to_device(&self.0)?, $attr_name, values) - } - }; -} - pub(crate) fn to_device(sys_path: &Path) -> Result { Device::from_syspath(sys_path) .map_err(|e| PlatformError::Udev("Couldn't transform syspath to device".to_string(), e)) diff --git a/rog-platform/src/macros.rs b/rog-platform/src/macros.rs new file mode 100644 index 00000000..244fdb94 --- /dev/null +++ b/rog-platform/src/macros.rs @@ -0,0 +1,131 @@ +#[macro_export] +macro_rules! has_attr { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = has_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self) -> bool { + match to_device(&self.$item) { + Ok(p) => crate::has_attr(&p, $attr_name), + Err(_) => false, + } + } + }); + }; +} + +#[macro_export] +macro_rules! watch_attr { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = monitor_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self) -> Result { + let mut path = self.$item.clone(); + path.push($attr_name); + let mut inotify = inotify::Inotify::init().unwrap(); + inotify.add_watch(path.to_str().unwrap(), inotify::WatchMask::MODIFY).unwrap(); + Ok(inotify) + } + }); + }; +} + +#[macro_export] +macro_rules! get_attr_bool { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = get_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self) -> Result { + crate::read_attr_bool(&to_device(&self.$item)?, $attr_name) + } + }); + }; +} + +#[macro_export] +macro_rules! set_attr_bool { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = set_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self, value: bool) -> Result<()> { + crate::write_attr_bool(&mut to_device(&self.$item)?, $attr_name, value) + } + }); + }; +} + +#[macro_export] +macro_rules! attr_bool { + ($attr_name:literal, $item:ident) => { + crate::has_attr!($attr_name $item); + crate::get_attr_bool!( $attr_name $item); + crate::set_attr_bool!($attr_name $item); + crate::watch_attr!($attr_name $item); + }; +} + +#[macro_export] +macro_rules! get_attr_u8 { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = get_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self) -> Result { + crate::read_attr_u8(&to_device(&self.$item)?, $attr_name) + } + }); + }; +} + +#[macro_export] +macro_rules! set_attr_u8 { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = set_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self, value: u8) -> Result<()> { + crate::write_attr_u8(&mut to_device(&self.$item)?, $attr_name, value) + } + }); + }; +} + +#[macro_export] +macro_rules! attr_u8 { + ($attr_name:literal, $item:ident) => { + crate::has_attr!($attr_name $item); + crate::get_attr_u8!($attr_name $item); + crate::set_attr_u8!($attr_name $item); + crate::watch_attr!($attr_name $item); + }; +} + +#[macro_export] +macro_rules! get_attr_u8_array { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = get_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self) -> Result> { + crate::read_attr_u8_array(&to_device(&self.$item)?, $attr_name) + } + }); + }; +} + +#[macro_export] +macro_rules! set_attr_u8_array { + ($(#[$doc_comment:meta])? $attr_name:literal $item:ident) => { + concat_idents::concat_idents!(fn_name = set_, $attr_name { + $(#[$doc_comment])* + pub fn fn_name(&self, values: &[u8]) -> Result<()> { + crate::write_attr_u8_array(&mut to_device(&self.$item)?, $attr_name, values) + } + }); + }; +} + +#[macro_export] +macro_rules! attr_u8_array { + ($attr_name:literal, $item:ident) => { + crate::has_attr!($attr_name $item); + crate::get_attr_u8_array!($attr_name $item); + crate::set_attr_u8_array!($attr_name $item); + }; +} diff --git a/rog-platform/src/platform.rs b/rog-platform/src/platform.rs index 81d906e7..62b13525 100644 --- a/rog-platform/src/platform.rs +++ b/rog-platform/src/platform.rs @@ -17,8 +17,10 @@ use crate::{ /// - dgpu_only /// - keyboard_mode, set keyboard RGB mode and speed /// - keyboard_state, set keyboard power states -#[derive(Debug, PartialEq, PartialOrd)] -pub struct AsusPlatform(PathBuf); +#[derive(Debug, PartialEq, PartialOrd, Clone)] +pub struct AsusPlatform { + path: PathBuf, +} impl AsusPlatform { pub fn new() -> Result { @@ -39,35 +41,19 @@ impl AsusPlatform { warn!("{}", err); PlatformError::Udev("scan_devices failed".into(), err) })? { - return Ok(Self(device.syspath().to_owned())); + return Ok(Self { + path: device.syspath().to_owned(), + }); } Err(PlatformError::MissingFunction( "asus-nb-wmi not found".into(), )) } - attr_bool!( - has_dgpu_disable, - get_dgpu_disable, - set_dgpu_disable, - "dgpu_disable" - ); - - attr_bool!( - has_egpu_enable, - get_egpu_enable, - set_egpu_enable, - "egpu_enable" - ); - - attr_bool!(has_panel_od, get_panel_od, set_panel_od, "panel_od"); - - attr_u8!( - has_gpu_mux_mode, - get_gpu_mux_mode, - set_gpu_mux_mode, - "gpu_mux_mode" - ); + attr_bool!("dgpu_disable", path); + attr_bool!("egpu_enable", path); + attr_bool!("panel_od", path); + attr_u8!("gpu_mux_mode", path); } #[derive(Serialize, Deserialize, Type, Debug, PartialEq, Clone, Copy)] diff --git a/rog-platform/src/power.rs b/rog-platform/src/power.rs new file mode 100644 index 00000000..fb7735b6 --- /dev/null +++ b/rog-platform/src/power.rs @@ -0,0 +1,62 @@ +use std::path::PathBuf; + +use log::warn; + +use crate::{ + attr_u8, + error::{PlatformError, Result}, + to_device, +}; + +/// The "platform" device provides access to things like: +/// - dgpu_disable +/// - egpu_enable +/// - panel_od +/// - dgpu_only +/// - keyboard_mode, set keyboard RGB mode and speed +/// - keyboard_state, set keyboard power states +#[derive(Debug, PartialEq, PartialOrd, Clone)] +pub struct AsusPower { + mains: PathBuf, + battery: PathBuf, + usb: Option, +} + +impl AsusPower { + pub fn new() -> Result { + let mut mains = PathBuf::new(); + let mut battery = PathBuf::new(); + let mut usb = None; + + let mut enumerator = udev::Enumerator::new().map_err(|err| { + warn!("{}", err); + PlatformError::Udev("enumerator failed".into(), err) + })?; + enumerator.match_subsystem("power_supply").map_err(|err| { + warn!("{}", err); + PlatformError::Udev("match_subsystem failed".into(), err) + })?; + + for device in enumerator.scan_devices().map_err(|err| { + warn!("{}", err); + PlatformError::Udev("scan_devices failed".into(), err) + })? { + if let Some(attr) = device.attribute_value("type") { + match attr.to_os_string().as_os_str().to_str() { + Some("Mains") => mains = device.syspath().to_path_buf(), + Some("Battery") => battery = device.syspath().to_path_buf(), + Some("USB") => usb = Some(device.syspath().to_path_buf()), + _ => {} + }; + } + } + + Ok(Self { + mains, + battery, + usb, + }) + } + + attr_u8!("charge_control_end_threshold", battery); +} diff --git a/rog-platform/src/supported.rs b/rog-platform/src/supported.rs index b94af610..161fdc64 100644 --- a/rog-platform/src/supported.rs +++ b/rog-platform/src/supported.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; use std::fmt; use zvariant_derive::Type; -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct SupportedFunctions { pub anime_ctrl: AnimeSupportedFunctions, pub charge_ctrl: ChargeSupportedFunctions, @@ -12,21 +12,21 @@ pub struct SupportedFunctions { pub rog_bios_ctrl: RogBiosSupportedFunctions, } -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct AnimeSupportedFunctions(pub bool); -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct ChargeSupportedFunctions { pub charge_level_set: bool, } -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct PlatformProfileFunctions { pub platform_profile: bool, pub fan_curves: bool, } -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct LedSupportedFunctions { pub prod_id: AuraDevice, pub brightness_set: bool, @@ -35,7 +35,7 @@ pub struct LedSupportedFunctions { pub per_key_led_mode: bool, } -#[derive(Serialize, Deserialize, Type, Debug, Default)] +#[derive(Serialize, Deserialize, Type, Debug, Default, Clone)] pub struct RogBiosSupportedFunctions { pub post_sound: bool, pub dgpu_only: bool,