diff --git a/CHANGELOG.md b/CHANGELOG.md index 629f9f47..c4df0bcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,36 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased ] +## [Unreleased - 4.5.0] +### Added +- intofy watches on: + - `charge_control_end_threshold` + - `panel_od` + - `gpu_mux_mode` + - `platform_profile` + - keyboard brightness + - These allow for updating any associated config and sending dbus notifications. +- New dbus methods + - `DgpuDisable` + - `SetDgpuDisable` + - `NotifyDgpuDisable` + - `EgpuEnable` + - `SetEgpuEnable` + - `NotifyEgpuEnable` + - `MainsOnline` (This is AC, check if plugged in or not) + - `NotifyMainsOnline` +### Changed +- Use loops to ensure that mutex is gained for LED changes. +- asusctl now uses tokio for async runtime. This helps simplify some code. +### Breaking +- DBUS: all charge control methods renamed to: + - `ChargeControlEndThreshold` + - `SetChargeControlEndThreshold` + - `NotifyChargeControlEndThreshold` + - `PanelOd` (form PanelOverdrive) + - `SetPanelOd` + - `NotifyPanelOd` + - Path `/org/asuslinux/Charge` changed to `/org/asuslinux/Power` ## [v4.4.0] - 2022-08-29 ### Added diff --git a/Cargo.lock b/Cargo.lock index 3fb39f30..ca9e659b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,9 +572,10 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "daemon" -version = "4.4.0" +version = "4.5.0-rc3" dependencies = [ "async-trait", + "concat-idents", "env_logger", "log", "logind-zbus", @@ -586,8 +587,8 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "smol", "sysfs-class", + "tokio", "toml", "zbus", "zvariant", @@ -1703,6 +1704,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.7" @@ -2528,18 +2539,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.20.1" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" dependencies = [ "autocfg", "libc", "mio", + "num_cpus", + "once_cell", "pin-project-lite", "socket2", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.9" diff --git a/asus-notify/src/main.rs b/asus-notify/src/main.rs index 7c02c917..bfd64f18 100644 --- a/asus-notify/src/main.rs +++ b/asus-notify/src/main.rs @@ -1,7 +1,7 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_aura::AuraEffect; use rog_dbus::{ - zbus_charge::ChargeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy, + zbus_led::LedProxy, zbus_platform::RogBiosProxy, zbus_power::PowerProxy, zbus_profile::ProfileProxy, }; use rog_profiles::Profile; @@ -54,7 +54,7 @@ fn main() -> Result<(), Box> { p.for_each(|e| { if let Ok(out) = e.args() { if let Ok(ref mut lock) = x.try_lock() { - notify!(do_post_sound_notif, lock, &out.sound()); + notify!(do_post_sound_notif, lock, &out.on()); } } future::ready(()) @@ -69,8 +69,8 @@ fn main() -> Result<(), Box> { executor .spawn(async move { let conn = zbus::Connection::system().await.unwrap(); - let proxy = ChargeProxy::new(&conn).await.unwrap(); - if let Ok(p) = proxy.receive_notify_charge().await { + let proxy = PowerProxy::new(&conn).await.unwrap(); + if let Ok(p) = proxy.receive_notify_charge_control_end_threshold().await { p.for_each(|e| { if let Ok(out) = e.args() { if let Ok(ref mut lock) = x.try_lock() { diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index dc8881bf..c3e127b2 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -211,7 +211,9 @@ fn do_parsed( } if let Some(chg_limit) = parsed.chg_limit { - dbus.proxies().charge().set_limit(chg_limit)?; + dbus.proxies() + .charge() + .set_charge_control_end_threshold(chg_limit)?; } Ok(()) @@ -803,10 +805,10 @@ fn handle_bios_option( } if let Some(opt) = cmd.panel_overdrive_set { - dbus.proxies().rog_bios().set_panel_overdrive(opt)?; + dbus.proxies().rog_bios().set_panel_od(opt)?; } if cmd.panel_overdrive_get { - let res = dbus.proxies().rog_bios().panel_overdrive()?; + let res = dbus.proxies().rog_bios().panel_od()?; println!("Panel overdrive on: {}", res); } } diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index c0c10929..5177b65a 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "daemon" -version = "4.4.0" +version = "4.5.0-rc3" license = "MPL-2.0" readme = "README.md" authors = ["Luke "] @@ -25,7 +25,7 @@ rog_profiles = { path = "../rog-profiles" } rog_dbus = { path = "../rog-dbus" } async-trait = "^0.1" -smol = "^1.2" +tokio = { version = "^1.21.1", features = ["macros", "rt-multi-thread", "time"]} # cli and logging log = "^0.4" @@ -43,3 +43,5 @@ toml = "^0.5.8" # Device control sysfs-class = "^0.1.2" # used for backlight control and baord ID + +concat-idents = "1.1.3" \ No newline at end of file diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs index 01291047..96959350 100644 --- a/daemon/src/ctrl_anime/mod.rs +++ b/daemon/src/ctrl_anime/mod.rs @@ -1,29 +1,19 @@ pub mod config; -pub mod zbus; +/// Implements CtrlTask, Reloadable, ZbusRun +pub mod trait_impls; -use async_trait::async_trait; +use self::config::{AnimeConfig, AnimeConfigCached}; +use crate::{error::RogError, GetSupported}; +use ::zbus::export::futures_util::lock::Mutex; use log::{error, info, warn}; use rog_anime::{ error::AnimeError, - usb::{ - get_anime_type, pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, - pkts_for_init, - }, + usb::{get_anime_type, pkt_for_flush, pkts_for_init}, ActionData, AnimeDataBuffer, AnimePacketType, AnimeType, }; use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw}; -use smol::Executor; use std::sync::atomic::{AtomicBool, Ordering}; -use std::{ - convert::TryFrom, - error::Error, - sync::{Arc, Mutex, MutexGuard}, - thread::sleep, -}; - -use crate::{error::RogError, GetSupported}; - -use self::config::{AnimeConfig, AnimeConfigCached}; +use std::{convert::TryFrom, error::Error, sync::Arc, thread::sleep}; impl GetSupported for CtrlAnime { type A = AnimeSupportedFunctions; @@ -85,6 +75,7 @@ impl CtrlAnime { // The only reason for this outer thread is to prevent blocking while waiting for the // next spawned thread to exit + // TODO: turn this in to async task (maybe? COuld still risk blocking main thread) std::thread::Builder::new() .name("AniMe system thread start".into()) .spawn(move || { @@ -95,7 +86,7 @@ impl CtrlAnime { let thread_running; let anime_type; loop { - if let Ok(lock) = inner.try_lock() { + if let Some(lock) = inner.try_lock() { thread_exit = lock.thread_exit.clone(); thread_running = lock.thread_running.clone(); anime_type = lock.anime_type; @@ -139,9 +130,10 @@ impl CtrlAnime { .ok(); false // Don't exit yet }) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - AnimeError::NoFrames + .map(|r| Ok(r)) + .unwrap_or_else(|| { + warn!("rog_anime::run_animation:callback failed"); + Err(AnimeError::NoFrames) }) }) { warn!("rog_anime::run_animation:Animation {}", err); @@ -150,7 +142,7 @@ impl CtrlAnime { } ActionData::Image(image) => { once = false; - if let Ok(lock) = inner.try_lock() { + if let Some(lock) = inner.try_lock() { lock.write_data_buffer(image.as_ref().clone()) .map_err(|e| error!("{}", e)) .ok(); @@ -171,7 +163,7 @@ impl CtrlAnime { } } // Clear the display on exit - if let Ok(lock) = inner.try_lock() { + if let Some(lock) = inner.try_lock() { if let Ok(data) = AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]) .map_err(|e| error!("{}", e)) @@ -216,85 +208,3 @@ impl CtrlAnime { Ok(()) } } - -pub struct CtrlAnimeTask { - inner: Arc>, -} - -impl CtrlAnimeTask { - pub async fn new(inner: Arc>) -> CtrlAnimeTask { - Self { inner } - } -} - -#[async_trait] -impl crate::CtrlTask for CtrlAnimeTask { - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - let run_action = - |start: bool, lock: MutexGuard, inner: Arc>| { - if start { - info!("CtrlAnimeTask running sleep animation"); - CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true); - } else { - info!("CtrlAnimeTask running wake animation"); - CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true); - } - }; - - let inner1 = self.inner.clone(); - let inner2 = self.inner.clone(); - let inner3 = self.inner.clone(); - let inner4 = self.inner.clone(); - self.create_sys_event_tasks( - executor, - // Loop is required to try an attempt to get the mutex *without* blocking - // other threads - it is possible to end up with deadlocks otherwise. - move || loop { - if let Ok(lock) = inner1.clone().try_lock() { - run_action(true, lock, inner1.clone()); - break; - } - }, - move || loop { - if let Ok(lock) = inner2.clone().try_lock() { - run_action(false, lock, inner2.clone()); - break; - } - }, - move || loop { - if let Ok(lock) = inner3.clone().try_lock() { - run_action(true, lock, inner3.clone()); - break; - } - }, - move || loop { - if let Ok(lock) = inner4.clone().try_lock() { - run_action(false, lock, inner4.clone()); - break; - } - }, - ) - .await; - - Ok(()) - } -} - -pub struct CtrlAnimeReloader(pub Arc>); - -impl crate::Reloadable for CtrlAnimeReloader { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(lock) = self.0.try_lock() { - lock.node - .write_bytes(&pkt_for_set_on(lock.config.awake_enabled))?; - lock.node.write_bytes(&pkt_for_apply())?; - lock.node - .write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?; - lock.node.write_bytes(&pkt_for_apply())?; - - let action = lock.cache.boot.clone(); - CtrlAnime::run_thread(self.0.clone(), action, true); - } - Ok(()) - } -} diff --git a/daemon/src/ctrl_anime/trait_impls.rs b/daemon/src/ctrl_anime/trait_impls.rs new file mode 100644 index 00000000..e005e4c0 --- /dev/null +++ b/daemon/src/ctrl_anime/trait_impls.rs @@ -0,0 +1,216 @@ +use super::CtrlAnime; +use crate::error::RogError; +use async_trait::async_trait; +use log::{info, warn}; +use rog_anime::{ + usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, + AnimeDataBuffer, AnimePowerStates, +}; +use std::sync::{atomic::Ordering, Arc}; +use zbus::{ + dbus_interface, + export::futures_util::lock::{Mutex, MutexGuard}, + Connection, SignalContext, +}; + +pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Anime"; + +#[derive(Clone)] +pub struct CtrlAnimeZbus(pub Arc>); + +/// The struct with the main dbus methods requires this trait +#[async_trait] +impl crate::ZbusRun for CtrlAnimeZbus { + async fn add_to_server(self, server: &mut Connection) { + Self::add_to_server_helper(self, ZBUS_PATH, server).await; + } +} + +// None of these calls can be guarnateed to succeed unless we loop until okay +// If the try_lock *does* succeed then any other thread trying to lock will not grab it +// until we finish. +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl CtrlAnimeZbus { + /// Writes a data stream of length. Will force system thread to exit until it is restarted + async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { + let lock = self.0.lock().await; + lock.thread_exit.store(true, Ordering::SeqCst); + lock.write_data_buffer(input).map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + err + })?; + Ok(()) + } + + /// Set the global AniMe brightness + async fn set_brightness(&self, bright: f32) { + let mut lock = self.0.lock().await; + let mut bright = bright; + if bright < 0.0 { + bright = 0.0 + } else if bright > 1.0 { + bright = 1.0; + } + lock.config.brightness = bright; + lock.config.write(); + } + + /// Set whether the AniMe is displaying images/data + async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) { + let mut lock = self.0.lock().await; + lock.node + .write_bytes(&pkt_for_set_on(status)) + .map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + }) + .ok(); + lock.config.awake_enabled = status; + lock.config.write(); + + Self::notify_power_states( + &ctxt, + AnimePowerStates { + brightness: lock.config.brightness.floor() as u8, + enabled: lock.config.awake_enabled, + boot_anim_enabled: lock.config.boot_anim_enabled, + }, + ) + .await + .ok(); + } + + /// Set whether the AniMe will show boot, suspend, or off animations + async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) { + let mut lock = self.0.lock().await; + lock.node + .write_bytes(&pkt_for_set_boot(on)) + .map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + }) + .ok(); + lock.node + .write_bytes(&pkt_for_apply()) + .map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + }) + .ok(); + lock.config.boot_anim_enabled = on; + lock.config.write(); + + Self::notify_power_states( + &ctxt, + AnimePowerStates { + brightness: lock.config.brightness.floor() as u8, + enabled: lock.config.awake_enabled, + boot_anim_enabled: lock.config.boot_anim_enabled, + }, + ) + .await + .ok(); + } + + /// The main loop is the base system set action if the user isn't running + /// the user daemon + async fn run_main_loop(&self, start: bool) { + if start { + let lock = self.0.lock().await; + lock.thread_exit.store(true, Ordering::SeqCst); + CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false); + } + } + + /// Get status of if the AniMe LEDs are on/displaying while system is awake + #[dbus_interface(property)] + async fn awake_enabled(&self) -> bool { + let lock = self.0.lock().await; + return lock.config.awake_enabled; + } + + /// Get the status of if factory system-status animations are enabled + #[dbus_interface(property)] + async fn boot_enabled(&self) -> bool { + let lock = self.0.lock().await; + return lock.config.boot_anim_enabled; + } + + /// Notify listeners of the status of AniMe LED power and factory system-status animations + #[dbus_interface(signal)] + async fn notify_power_states( + ctxt: &SignalContext<'_>, + data: AnimePowerStates, + ) -> zbus::Result<()>; +} + +#[async_trait] +impl crate::CtrlTask for CtrlAnimeZbus { + fn zbus_path() -> &'static str { + ZBUS_PATH + } + + async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { + let run_action = + |start: bool, lock: MutexGuard, inner: Arc>| { + if start { + info!("CtrlAnimeTask running sleep animation"); + CtrlAnime::run_thread(inner.clone(), lock.cache.shutdown.clone(), true); + } else { + info!("CtrlAnimeTask running wake animation"); + CtrlAnime::run_thread(inner.clone(), lock.cache.wake.clone(), true); + } + }; + + let inner1 = self.0.clone(); + let inner2 = self.0.clone(); + let inner3 = self.0.clone(); + let inner4 = self.0.clone(); + self.create_sys_event_tasks( + // Loop is required to try an attempt to get the mutex *without* blocking + // other threads - it is possible to end up with deadlocks otherwise. + move || loop { + if let Some(lock) = inner1.try_lock() { + run_action(true, lock, inner1.clone()); + break; + } + }, + move || loop { + if let Some(lock) = inner2.try_lock() { + run_action(false, lock, inner2.clone()); + break; + } + }, + move || loop { + if let Some(lock) = inner3.try_lock() { + run_action(true, lock, inner3.clone()); + break; + } + }, + move || loop { + if let Some(lock) = inner4.try_lock() { + run_action(false, lock, inner4.clone()); + break; + } + }, + ) + .await; + + Ok(()) + } +} + +#[async_trait] +impl crate::Reloadable for CtrlAnimeZbus { + async fn reload(&mut self) -> Result<(), RogError> { + if let Some(lock) = self.0.try_lock() { + lock.node + .write_bytes(&pkt_for_set_on(lock.config.awake_enabled))?; + lock.node.write_bytes(&pkt_for_apply())?; + lock.node + .write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled))?; + lock.node.write_bytes(&pkt_for_apply())?; + + let action = lock.cache.boot.clone(); + CtrlAnime::run_thread(self.0.clone(), action, true); + } + Ok(()) + } +} diff --git a/daemon/src/ctrl_anime/zbus.rs b/daemon/src/ctrl_anime/zbus.rs deleted file mode 100644 index a71c3600..00000000 --- a/daemon/src/ctrl_anime/zbus.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use async_trait::async_trait; -use log::warn; -use rog_anime::{ - usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, - AnimeDataBuffer, AnimePowerStates, -}; -use zbus::{dbus_interface, Connection, SignalContext}; - -use std::sync::atomic::Ordering; - -use super::CtrlAnime; - -pub struct CtrlAnimeZbus(pub Arc>); - -/// The struct with the main dbus methods requires this trait -#[async_trait] -impl crate::ZbusAdd for CtrlAnimeZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await; - } -} - -// None of these calls can be guarnateed to succeed unless we loop until okay -// If the try_lock *does* succeed then any other thread trying to lock will not grab it -// until we finish. -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlAnimeZbus { - /// Writes a data stream of length. Will force system thread to exit until it is restarted - fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { - 'outer: loop { - if let Ok(lock) = self.0.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - lock.write_data_buffer(input).map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - err - })?; - break 'outer; - } - } - Ok(()) - } - - /// Set the global AniMe brightness - fn set_brightness(&self, bright: f32) { - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - let mut bright = bright; - if bright < 0.0 { - bright = 0.0 - } else if bright > 1.0 { - bright = 1.0; - } - lock.config.brightness = bright; - lock.config.write(); - break 'outer; - } - } - } - - /// Set whether the AniMe is displaying images/data - async fn set_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, status: bool) { - let states; - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - lock.node - .write_bytes(&pkt_for_set_on(status)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.awake_enabled = status; - lock.config.write(); - - states = Some(AnimePowerStates { - brightness: lock.config.brightness.floor() as u8, - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, - }); - break 'outer; - } - } - if let Some(state) = states { - Self::notify_power_states(&ctxt, state).await.ok(); - } - } - - /// Set whether the AniMe will show boot, suspend, or off animations - async fn set_boot_on_off(&self, #[zbus(signal_context)] ctxt: SignalContext<'_>, on: bool) { - let states; - 'outer: loop { - if let Ok(mut lock) = self.0.try_lock() { - lock.node - .write_bytes(&pkt_for_set_boot(on)) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.node - .write_bytes(&pkt_for_apply()) - .map_err(|err| { - warn!("rog_anime::run_animation:callback {}", err); - }) - .ok(); - lock.config.boot_anim_enabled = on; - lock.config.write(); - - states = Some(AnimePowerStates { - brightness: lock.config.brightness.floor() as u8, - enabled: lock.config.awake_enabled, - boot_anim_enabled: lock.config.boot_anim_enabled, - }); - break 'outer; - } - } - if let Some(state) = states { - Self::notify_power_states(&ctxt, state).await.ok(); - } - } - - /// The main loop is the base system set action if the user isn't running - /// the user daemon - fn run_main_loop(&self, start: bool) { - if start { - 'outer: loop { - if let Ok(lock) = self.0.try_lock() { - lock.thread_exit.store(true, Ordering::SeqCst); - CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false); - break 'outer; - } - } - } - } - - /// Get status of if the AniMe LEDs are on/displaying while system is awake - #[dbus_interface(property)] - fn awake_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.awake_enabled; - } - true - } - - /// Get the status of if factory system-status animations are enabled - #[dbus_interface(property)] - fn boot_enabled(&self) -> bool { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.boot_anim_enabled; - } - true - } - - /// Notify listeners of the status of AniMe LED power and factory system-status animations - #[dbus_interface(signal)] - async fn notify_power_states( - ctxt: &SignalContext<'_>, - data: AnimePowerStates, - ) -> zbus::Result<()>; -} diff --git a/daemon/src/ctrl_aura/config.rs b/daemon/src/ctrl_aura/config.rs index 428e3cab..50098af8 100644 --- a/daemon/src/ctrl_aura/config.rs +++ b/daemon/src/ctrl_aura/config.rs @@ -114,7 +114,7 @@ impl From<&AuraPowerConfig> for AuraPowerDev { } #[derive(Deserialize, Serialize)] -#[serde(default)] +// #[serde(default)] pub struct AuraConfig { pub brightness: LedBrightness, pub current_mode: AuraModeNum, @@ -271,7 +271,7 @@ impl AuraConfig { if l == 0 { warn!("File is empty {}", AURA_CONFIG_PATH); } else { - let x: AuraConfig = serde_json::from_str(&buf) + let x = serde_json::from_str(&buf) .unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH)); *self = x; } diff --git a/daemon/src/ctrl_aura/controller.rs b/daemon/src/ctrl_aura/controller.rs index b7f09336..4ad21fad 100644 --- a/daemon/src/ctrl_aura/controller.rs +++ b/daemon/src/ctrl_aura/controller.rs @@ -1,21 +1,15 @@ use crate::{ error::RogError, laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES}, - CtrlTask, }; -use async_trait::async_trait; -use log::{error, info, warn}; +use log::{info, warn}; use rog_aura::{ usb::{AuraDevice, LED_APPLY, LED_SET}, AuraEffect, KeyColourArray, LedBrightness, PerKeyRaw, LED_MSG_LEN, }; use rog_aura::{AuraZone, Direction, Speed, GRADIENT}; use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions}; -use smol::Executor; use std::collections::BTreeMap; -use std::sync::Arc; -use std::sync::Mutex; -use std::sync::MutexGuard; use crate::GetSupported; @@ -74,104 +68,6 @@ pub struct CtrlKbdLed { pub config: AuraConfig, } -pub struct CtrlKbdLedTask { - inner: Arc>, -} - -impl CtrlKbdLedTask { - pub fn new(inner: Arc>) -> Self { - Self { inner } - } - - fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { - let bright = lock.kd_brightness.get_brightness()?; - lock.config.read(); - lock.config.brightness = (bright as u32).into(); - lock.config.write(); - return Ok(()); - } -} - -#[async_trait] -impl CtrlTask for CtrlKbdLedTask { - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - let load_save = |start: bool, mut lock: MutexGuard| { - // If waking up - if !start { - info!("CtrlKbdLedTask reloading brightness and modes"); - lock.set_brightness(lock.config.brightness) - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - lock.write_current_config_mode() - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - } else if start { - info!("CtrlKbdLedTask saving last brightness"); - Self::update_config(&mut lock) - .map_err(|e| error!("CtrlKbdLedTask: {e}")) - .ok(); - } - }; - - let inner1 = self.inner.clone(); - let inner2 = self.inner.clone(); - let inner3 = self.inner.clone(); - let inner4 = self.inner.clone(); - self.create_sys_event_tasks( - executor, - // Loop so that we do aquire the lock but also don't block other - // threads (prevents potential deadlocks) - move || loop { - if let Ok(lock) = inner1.clone().try_lock() { - load_save(true, lock); - break; - } - }, - move || loop { - if let Ok(lock) = inner2.clone().try_lock() { - load_save(false, lock); - break; - } - }, - move || loop { - if let Ok(lock) = inner3.clone().try_lock() { - load_save(false, lock); - break; - } - }, - move || loop { - if let Ok(lock) = inner4.clone().try_lock() { - load_save(false, lock); - break; - } - }, - ) - .await; - - Ok(()) - } -} - -pub struct CtrlKbdLedReloader(pub Arc>); - -impl crate::Reloadable for CtrlKbdLedReloader { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.write_current_config_mode()?; - ctrl.set_power_states().map_err(|err| warn!("{err}")).ok(); - } - Ok(()) - } -} - -pub struct CtrlKbdLedZbus(pub Arc>); - -impl CtrlKbdLedZbus { - pub fn new(inner: Arc>) -> Self { - Self(inner) - } -} - impl CtrlKbdLed { pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result { let mut led_prod = None; @@ -393,7 +289,7 @@ impl CtrlKbdLed { Ok(()) } - fn write_current_config_mode(&mut self) -> Result<(), RogError> { + pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> { if self.config.multizone_on { let mode = self.config.current_mode; let mut create = false; diff --git a/daemon/src/ctrl_aura/mod.rs b/daemon/src/ctrl_aura/mod.rs index 85a4d229..9c365c20 100644 --- a/daemon/src/ctrl_aura/mod.rs +++ b/daemon/src/ctrl_aura/mod.rs @@ -1,3 +1,4 @@ pub mod config; pub mod controller; -pub mod zbus; +/// Implements CtrlTask, Reloadable, ZbusRun +pub mod trait_impls; diff --git a/daemon/src/ctrl_aura/trait_impls.rs b/daemon/src/ctrl_aura/trait_impls.rs new file mode 100644 index 00000000..39b24afd --- /dev/null +++ b/daemon/src/ctrl_aura/trait_impls.rs @@ -0,0 +1,320 @@ +use async_trait::async_trait; +use log::{error, info, warn}; +use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw}; +use std::{collections::BTreeMap, sync::Arc}; +use zbus::{ + dbus_interface, + export::futures_util::{ + lock::{Mutex, MutexGuard}, + StreamExt, + }, + Connection, SignalContext, +}; + +use crate::{error::RogError, CtrlTask}; + +use super::controller::CtrlKbdLed; + +pub(super) const ZBUS_PATH: &str = "/org/asuslinux/Aura"; + +#[derive(Clone)] +pub struct CtrlKbdLedZbus(pub Arc>); + +impl CtrlKbdLedZbus { + fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> { + let bright = lock.kd_brightness.get_brightness()?; + lock.config.read(); + lock.config.brightness = (bright as u32).into(); + lock.config.write(); + return Ok(()); + } +} + +#[async_trait] +impl crate::ZbusRun for CtrlKbdLedZbus { + async fn add_to_server(self, server: &mut Connection) { + Self::add_to_server_helper(self, ZBUS_PATH, server).await; + } +} + +/// The main interface for changing, reading, or notfying signals +/// +/// LED commands are split between Brightness, Modes, Per-Key +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl CtrlKbdLedZbus { + /// Set the keyboard brightness level (0-3) + async fn set_brightness(&mut self, brightness: LedBrightness) { + let ctrl = self.0.lock().await; + ctrl.set_brightness(brightness) + .map_err(|err| warn!("{}", err)) + .ok(); + } + + /// Set a variety of states, input is array of enum. + /// `enabled` sets if the sent array should be disabled or enabled + /// + /// ```text + /// pub struct AuraPowerDev { + /// pub x1866: Vec, + /// pub x19b6: Vec, + /// } + /// pub enum AuraDev1866 { + /// Awake, + /// Keyboard, + /// Lightbar, + /// Boot, + /// Sleep, + /// } + /// enum AuraDev19b6 { + /// BootLogo, + /// BootKeyb, + /// AwakeLogo, + /// AwakeKeyb, + /// SleepLogo, + /// SleepKeyb, + /// ShutdownLogo, + /// ShutdownKeyb, + /// AwakeBar, + /// BootBar, + /// SleepBar, + /// ShutdownBar, + /// } + /// ``` + async fn set_leds_power( + &mut self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + options: AuraPowerDev, + enabled: bool, + ) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + for p in options.tuf { + ctrl.config.enabled.set_tuf(p, enabled); + } + for p in options.x1866 { + ctrl.config.enabled.set_0x1866(p, enabled); + } + for p in options.x19b6 { + ctrl.config.enabled.set_0x19b6(p, enabled); + } + + ctrl.config.write(); + + ctrl.set_power_states().map_err(|e| { + warn!("{}", e); + e + })?; + + Self::notify_power_states(&ctxt, &AuraPowerDev::from(&ctrl.config.enabled)) + .await + .unwrap_or_else(|err| warn!("{}", err)); + Ok(()) + } + + async fn set_led_mode( + &mut self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + effect: AuraEffect, + ) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + + ctrl.set_effect(effect).map_err(|e| { + warn!("{}", e); + e + })?; + + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + Self::notify_led(&ctxt, mode.clone()) + .await + .unwrap_or_else(|err| warn!("{}", err)); + } + Ok(()) + } + + async fn next_led_mode( + &self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + ) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + + ctrl.toggle_mode(false).map_err(|e| { + warn!("{}", e); + e + })?; + + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + Self::notify_led(&ctxt, mode.clone()) + .await + .unwrap_or_else(|err| warn!("{}", err)); + } + + Ok(()) + } + + async fn prev_led_mode( + &self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + ) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + + ctrl.toggle_mode(true).map_err(|e| { + warn!("{}", e); + e + })?; + + if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { + Self::notify_led(&ctxt, mode.clone()) + .await + .unwrap_or_else(|err| warn!("{}", err)); + } + + Ok(()) + } + + async fn next_led_brightness(&self) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.next_brightness().map_err(|e| { + warn!("{}", e); + e + })?; + Ok(()) + } + + async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.prev_brightness().map_err(|e| { + warn!("{}", e); + e + })?; + Ok(()) + } + + // As property doesn't work for AuraPowerDev (complexity of serialization?) + // #[dbus_interface(property)] + async fn leds_enabled(&self) -> AuraPowerDev { + let ctrl = self.0.lock().await; + return AuraPowerDev::from(&ctrl.config.enabled); + } + + /// Return the current mode data + async fn led_mode(&self) -> AuraModeNum { + let ctrl = self.0.lock().await; + return ctrl.config.current_mode; + } + + /// Return a list of available modes + async fn led_modes(&self) -> BTreeMap { + let ctrl = self.0.lock().await; + return ctrl.config.builtins.clone(); + } + + async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.write_effect_block(&data)?; + Ok(()) + } + + /// Return the current LED brightness + #[dbus_interface(property)] + async fn led_brightness(&self) -> i8 { + let ctrl = self.0.lock().await; + ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1) + } + + #[dbus_interface(signal)] + async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>; + + #[dbus_interface(signal)] + async fn notify_power_states( + signal_ctxt: &SignalContext<'_>, + data: &AuraPowerDev, + ) -> zbus::Result<()>; +} + +#[async_trait] +impl CtrlTask for CtrlKbdLedZbus { + fn zbus_path() -> &'static str { + ZBUS_PATH + } + + async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> { + let load_save = |start: bool, mut lock: MutexGuard| { + // If waking up + if !start { + info!("CtrlKbdLedTask reloading brightness and modes"); + lock.set_brightness(lock.config.brightness) + .map_err(|e| error!("CtrlKbdLedTask: {e}")) + .ok(); + lock.write_current_config_mode() + .map_err(|e| error!("CtrlKbdLedTask: {e}")) + .ok(); + } else if start { + info!("CtrlKbdLedTask saving last brightness"); + Self::update_config(&mut lock) + .map_err(|e| error!("CtrlKbdLedTask: {e}")) + .ok(); + } + }; + + let inner1 = self.0.clone(); + let inner2 = self.0.clone(); + let inner3 = self.0.clone(); + let inner4 = self.0.clone(); + self.create_sys_event_tasks( + // Loop so that we do aquire the lock but also don't block other + // threads (prevents potential deadlocks) + move || loop { + if let Some(lock) = inner1.try_lock() { + load_save(true, lock); + break; + } + }, + move || loop { + if let Some(lock) = inner2.try_lock() { + load_save(false, lock); + break; + } + }, + move || loop { + if let Some(lock) = inner3.try_lock() { + load_save(true, lock); + break; + } + }, + move || loop { + if let Some(lock) = inner4.try_lock() { + load_save(false, lock); + break; + } + }, + ) + .await; + + let ctrl2 = self.0.clone(); + let ctrl = self.0.lock().await; + let mut watch = ctrl.kd_brightness.monitor_brightness()?; + tokio::spawn(async move { + let mut buffer = [0; 32]; + watch + .event_stream(&mut buffer) + .unwrap() + .for_each(|_| async { + if let Some(lock) = ctrl2.try_lock() { + load_save(true, lock); + } + }) + .await; + }); + + Ok(()) + } +} + +#[async_trait] +impl crate::Reloadable for CtrlKbdLedZbus { + async fn reload(&mut self) -> Result<(), RogError> { + let mut ctrl = self.0.lock().await; + ctrl.write_current_config_mode()?; + ctrl.set_power_states().map_err(|err| warn!("{err}")).ok(); + Ok(()) + } +} diff --git a/daemon/src/ctrl_aura/zbus.rs b/daemon/src/ctrl_aura/zbus.rs deleted file mode 100644 index 4abc406e..00000000 --- a/daemon/src/ctrl_aura/zbus.rs +++ /dev/null @@ -1,238 +0,0 @@ -use std::collections::BTreeMap; - -use async_trait::async_trait; -use log::warn; -use rog_aura::{usb::AuraPowerDev, AuraEffect, AuraModeNum, LedBrightness, PerKeyRaw}; -use zbus::{dbus_interface, Connection, SignalContext}; - -use super::controller::CtrlKbdLedZbus; - -#[async_trait] -impl crate::ZbusAdd for CtrlKbdLedZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Aura", server).await; - } -} - -/// The main interface for changing, reading, or notfying signals -/// -/// LED commands are split between Brightness, Modes, Per-Key -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlKbdLedZbus { - /// Set the keyboard brightness level (0-3) - async fn set_brightness(&mut self, brightness: LedBrightness) { - if let Ok(ctrl) = self.0.try_lock() { - ctrl.set_brightness(brightness) - .map_err(|err| warn!("{}", err)) - .ok(); - } - } - - /// Set a variety of states, input is array of enum. - /// `enabled` sets if the sent array should be disabled or enabled - /// - /// ```text - /// pub struct AuraPowerDev { - /// pub x1866: Vec, - /// pub x19b6: Vec, - /// } - /// pub enum AuraDev1866 { - /// Awake, - /// Keyboard, - /// Lightbar, - /// Boot, - /// Sleep, - /// } - /// enum AuraDev19b6 { - /// BootLogo, - /// BootKeyb, - /// AwakeLogo, - /// AwakeKeyb, - /// SleepLogo, - /// SleepKeyb, - /// ShutdownLogo, - /// ShutdownKeyb, - /// AwakeBar, - /// BootBar, - /// SleepBar, - /// ShutdownBar, - /// } - /// ``` - async fn set_leds_power( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - options: AuraPowerDev, - enabled: bool, - ) -> zbus::fdo::Result<()> { - let mut states = None; - if let Ok(mut ctrl) = self.0.try_lock() { - for p in options.tuf { - ctrl.config.enabled.set_tuf(p, enabled); - } - for p in options.x1866 { - ctrl.config.enabled.set_0x1866(p, enabled); - } - for p in options.x19b6 { - ctrl.config.enabled.set_0x19b6(p, enabled); - } - - ctrl.config.write(); - - ctrl.set_power_states().map_err(|e| { - warn!("{}", e); - e - })?; - - states = Some(AuraPowerDev::from(&ctrl.config.enabled)); - } - // Need to pull state out like this due to MutexGuard - if let Some(states) = states { - Self::notify_power_states(&ctxt, &states) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - Ok(()) - } - - async fn set_led_mode( - &mut self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - effect: AuraEffect, - ) -> zbus::fdo::Result<()> { - let mut led = None; - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.set_effect(effect).map_err(|e| { - warn!("{}", e); - e - })?; - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - led = Some(mode.clone()); - } - } - if let Some(led) = led { - Self::notify_led(&ctxt, led) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - Ok(()) - } - - async fn next_led_mode( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - ) -> zbus::fdo::Result<()> { - let mut led = None; - if let Ok(mut ctrl) = self.0.lock() { - ctrl.toggle_mode(false).map_err(|e| { - warn!("{}", e); - e - })?; - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - led = Some(mode.clone()); - } - } - if let Some(led) = led { - Self::notify_led(&ctxt, led) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - Ok(()) - } - - async fn prev_led_mode( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - ) -> zbus::fdo::Result<()> { - let mut led = None; - if let Ok(mut ctrl) = self.0.lock() { - ctrl.toggle_mode(true).map_err(|e| { - warn!("{}", e); - e - })?; - - if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) { - led = Some(mode.clone()); - } - } - if let Some(led) = led { - Self::notify_led(&ctxt, led) - .await - .unwrap_or_else(|err| warn!("{}", err)); - } - Ok(()) - } - - async fn next_led_brightness(&self) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.next_brightness().map_err(|e| { - warn!("{}", e); - e - })?; - } - Ok(()) - } - - async fn prev_led_brightness(&self) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.prev_brightness().map_err(|e| { - warn!("{}", e); - e - })?; - } - Ok(()) - } - - // As property doesn't work for AuraPowerDev (complexity of serialization?) - // #[dbus_interface(property)] - async fn leds_enabled(&self) -> AuraPowerDev { - loop { - if let Ok(ctrl) = self.0.try_lock() { - return AuraPowerDev::from(&ctrl.config.enabled); - } - } - } - - /// Return the current mode data - async fn led_mode(&self) -> AuraModeNum { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.current_mode; - } - AuraModeNum::Static - } - - /// Return a list of available modes - async fn led_modes(&self) -> BTreeMap { - loop { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.config.builtins.clone(); - } - } - } - - async fn per_key_raw(&self, data: PerKeyRaw) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.0.try_lock() { - ctrl.write_effect_block(&data)?; - } - Ok(()) - } - - /// Return the current LED brightness - #[dbus_interface(property)] - async fn led_brightness(&self) -> i8 { - if let Ok(ctrl) = self.0.try_lock() { - return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1); - } - warn!("SetKeyBacklight could not serialise"); - -1 - } - - #[dbus_interface(signal)] - async fn notify_led(signal_ctxt: &SignalContext<'_>, data: AuraEffect) -> zbus::Result<()>; - - #[dbus_interface(signal)] - async fn notify_power_states( - signal_ctxt: &SignalContext<'_>, - data: &AuraPowerDev, - ) -> zbus::Result<()>; -} diff --git a/daemon/src/ctrl_platform.rs b/daemon/src/ctrl_platform.rs index fa3e251d..77063d30 100644 --- a/daemon/src/ctrl_platform.rs +++ b/daemon/src/ctrl_platform.rs @@ -1,29 +1,29 @@ -use crate::CtrlTask; use crate::{config::Config, error::RogError, GetSupported}; +use crate::{task_watch_item, CtrlTask}; use async_trait::async_trait; use log::{info, warn}; use rog_platform::platform::{AsusPlatform, GpuMode}; use rog_platform::supported::RogBiosSupportedFunctions; -use smol::Executor; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::Path; use std::process::Command; use std::sync::Arc; -use std::sync::Mutex; +use zbus::export::futures_util::lock::Mutex; use zbus::Connection; use zbus::{dbus_interface, SignalContext}; -static ASUS_POST_LOGO_SOUND: &str = +const ZBUS_PATH: &str = "/org/asuslinux/Platform"; +const ASUS_POST_LOGO_SOUND: &str = "/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e"; #[derive(Clone)] -pub struct CtrlRogBios { +pub struct CtrlPlatform { platform: AsusPlatform, config: Arc>, } -impl GetSupported for CtrlRogBios { +impl GetSupported for CtrlPlatform { type A = RogBiosSupportedFunctions; fn get_supported() -> Self::A { @@ -49,7 +49,7 @@ impl GetSupported for CtrlRogBios { } } -impl CtrlRogBios { +impl CtrlPlatform { pub fn new(config: Arc>) -> Result { let platform = AsusPlatform::new()?; @@ -59,12 +59,12 @@ impl CtrlRogBios { } if Path::new(ASUS_POST_LOGO_SOUND).exists() { - CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?; + CtrlPlatform::set_path_mutable(ASUS_POST_LOGO_SOUND)?; } else { info!("Switch for POST boot sound not detected"); } - Ok(CtrlRogBios { platform, config }) + Ok(CtrlPlatform { platform, config }) } fn set_path_mutable(path: &str) -> Result<(), RogError> { @@ -129,7 +129,7 @@ impl CtrlRogBios { Ok(()) } - fn set_panel_od(&self, enable: bool) -> Result<(), RogError> { + fn set_panel_overdrive(&self, enable: bool) -> Result<(), RogError> { self.platform.set_panel_od(enable).map_err(|err| { warn!("CtrlRogBios: set_panel_overdrive {}", err); err @@ -139,7 +139,7 @@ impl CtrlRogBios { } #[dbus_interface(name = "org.asuslinux.Daemon")] -impl CtrlRogBios { +impl CtrlPlatform { async fn set_gpu_mux_mode( &mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>, @@ -197,12 +197,13 @@ impl CtrlRogBios { #[dbus_interface(signal)] async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {} - async fn set_panel_overdrive( + async fn set_panel_od( &mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>, overdrive: bool, ) { if self + .platform .set_panel_od(overdrive) .map_err(|err| { warn!("CtrlRogBios: set_panel_overdrive {}", err); @@ -210,67 +211,149 @@ impl CtrlRogBios { }) .is_ok() { - if let Ok(mut lock) = self.config.try_lock() { + if let Some(mut lock) = self.config.try_lock() { lock.panel_od = overdrive; lock.write(); } - Self::notify_panel_overdrive(&ctxt, overdrive).await.ok(); + Self::notify_panel_od(&ctxt, overdrive).await.ok(); } } - fn panel_overdrive(&self) -> bool { - self.platform + /// Get the `panel_od` value from platform. Updates the stored value in internal config also. + fn panel_od(&self) -> bool { + let od = self + .platform .get_panel_od() .map_err(|err| { - warn!("CtrlRogBios: get panel overdrive {}", err); + warn!("CtrlRogBios: get_panel_od {}", err); + err + }) + .unwrap_or(false); + if let Some(mut lock) = self.config.try_lock() { + lock.panel_od = od; + lock.write(); + } + od + } + + #[dbus_interface(signal)] + async fn notify_panel_od(signal_ctxt: &SignalContext<'_>, overdrive: bool) -> zbus::Result<()> { + } + + async fn set_dgpu_disable( + &mut self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + disable: bool, + ) { + if self + .platform + .set_dgpu_disable(disable) + .map_err(|err| { + warn!("CtrlRogBios: set_dgpu_disable {}", err); + err + }) + .is_ok() + { + Self::notify_dgpu_disable(&ctxt, disable).await.ok(); + } + } + + fn dgpu_disable(&self) -> bool { + self.platform + .get_dgpu_disable() + .map_err(|err| { + warn!("CtrlRogBios: get_dgpu_disable {}", err); err }) .unwrap_or(false) } #[dbus_interface(signal)] - async fn notify_panel_overdrive( + async fn notify_dgpu_disable( signal_ctxt: &SignalContext<'_>, - overdrive: bool, + disable: bool, ) -> zbus::Result<()> { } + + async fn set_egpu_enable( + &mut self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + enable: bool, + ) { + if self + .platform + .set_egpu_enable(enable) + .map_err(|err| { + warn!("CtrlRogBios: set_egpu_enable {}", err); + err + }) + .is_ok() + { + Self::notify_egpu_enable(&ctxt, enable).await.ok(); + } + } + + fn egpu_enable(&self) -> bool { + self.platform + .get_egpu_enable() + .map_err(|err| { + warn!("CtrlRogBios: get_egpu_enable {}", err); + err + }) + .unwrap_or(false) + } + + #[dbus_interface(signal)] + async fn notify_egpu_enable(signal_ctxt: &SignalContext<'_>, enable: bool) -> zbus::Result<()> { + } } #[async_trait] -impl crate::ZbusAdd for CtrlRogBios { +impl crate::ZbusRun for CtrlPlatform { async fn add_to_server(self, server: &mut Connection) { Self::add_to_server_helper(self, "/org/asuslinux/Platform", server).await; } } -impl crate::Reloadable for CtrlRogBios { - fn reload(&mut self) -> Result<(), RogError> { +#[async_trait] +impl crate::Reloadable for CtrlPlatform { + async fn reload(&mut self) -> Result<(), RogError> { if self.platform.has_panel_od() { - let p = if let Ok(lock) = self.config.try_lock() { + let p = if let Some(lock) = self.config.try_lock() { lock.panel_od } else { false }; - self.set_panel_od(p)?; + self.set_panel_overdrive(p)?; } Ok(()) } } +impl CtrlPlatform { + task_watch_item!(panel_od platform); + task_watch_item!(dgpu_disable platform); + task_watch_item!(egpu_enable platform); + task_watch_item!(gpu_mux_mode platform); +} + #[async_trait] -impl CtrlTask for CtrlRogBios { - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { +impl CtrlTask for CtrlPlatform { + fn zbus_path() -> &'static str { + ZBUS_PATH + } + + async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { let platform1 = self.clone(); let platform2 = self.clone(); self.create_sys_event_tasks( - executor, move || {}, move || { info!("CtrlRogBios reloading panel_od"); - if let Ok(lock) = platform1.config.try_lock() { + if let Some(lock) = platform1.config.try_lock() { if platform1.platform.has_panel_od() { platform1 - .set_panel_od(lock.panel_od) + .set_panel_overdrive(lock.panel_od) .map_err(|err| { warn!("CtrlCharge: set_limit {}", err); err @@ -282,10 +365,10 @@ impl CtrlTask for CtrlRogBios { move || {}, move || { info!("CtrlRogBios reloading panel_od"); - if let Ok(lock) = platform2.config.try_lock() { + if let Some(lock) = platform2.config.try_lock() { if platform2.platform.has_panel_od() { platform2 - .set_panel_od(lock.panel_od) + .set_panel_overdrive(lock.panel_od) .map_err(|err| { warn!("CtrlCharge: set_limit {}", err); err @@ -297,6 +380,11 @@ impl CtrlTask for CtrlRogBios { ) .await; + self.watch_panel_od(signal_ctxt.clone()).await?; + self.watch_dgpu_disable(signal_ctxt.clone()).await?; + self.watch_egpu_enable(signal_ctxt.clone()).await?; + self.watch_gpu_mux_mode(signal_ctxt.clone()).await?; + Ok(()) } } diff --git a/daemon/src/ctrl_power.rs b/daemon/src/ctrl_power.rs index 6f01fd30..dab60300 100644 --- a/daemon/src/ctrl_power.rs +++ b/daemon/src/ctrl_power.rs @@ -1,16 +1,18 @@ -use crate::CtrlTask; use crate::{config::Config, error::RogError, GetSupported}; +use crate::{task_watch_item, CtrlTask}; use async_trait::async_trait; use log::{info, warn}; use rog_platform::power::AsusPower; use rog_platform::supported::ChargeSupportedFunctions; -use smol::Executor; use std::sync::Arc; -use std::sync::Mutex; use zbus::dbus_interface; +use zbus::export::futures_util::lock::Mutex; +use zbus::export::futures_util::StreamExt; use zbus::Connection; use zbus::SignalContext; +const ZBUS_PATH: &str = "/org/asuslinux/Power"; + impl GetSupported for CtrlPower { type A = ChargeSupportedFunctions; @@ -33,7 +35,7 @@ pub struct CtrlPower { #[dbus_interface(name = "org.asuslinux.Daemon")] impl CtrlPower { - async fn set_limit( + async fn set_charge_control_end_threshold( &mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>, limit: u8, @@ -47,31 +49,62 @@ impl CtrlPower { err }) .ok(); - Self::notify_charge(&ctxt, limit).await?; + Self::notify_charge_control_end_threshold(&ctxt, limit).await?; Ok(()) } - fn limit(&self) -> i8 { - if let Ok(config) = self.config.try_lock() { - return config.bat_charge_limit as i8; + fn charge_control_end_threshold(&self) -> u8 { + loop { + if let Some(config) = self.config.try_lock() { + let limit = self + .power + .get_charge_control_end_threshold() + .map_err(|err| { + warn!("CtrlCharge: get_charge_control_end_threshold {}", err); + err + }) + .unwrap_or(100); + if let Some(mut config) = self.config.try_lock() { + config.read(); + config.bat_charge_limit = limit; + config.write(); + } + + return config.bat_charge_limit; + } } - -1 + } + + fn mains_online(&self) -> bool { + if self.power.has_online() { + if let Ok(v) = self.power.get_online() { + return v == 1; + } + } + false } #[dbus_interface(signal)] - async fn notify_charge(ctxt: &SignalContext<'_>, limit: u8) -> zbus::Result<()>; + async fn notify_charge_control_end_threshold( + ctxt: &SignalContext<'_>, + limit: u8, + ) -> zbus::Result<()>; + + #[dbus_interface(signal)] + async fn notify_mains_online(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()>; } #[async_trait] -impl crate::ZbusAdd for CtrlPower { +impl crate::ZbusRun for CtrlPower { async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Charge", server).await; + Self::add_to_server_helper(self, ZBUS_PATH, server).await; } } +#[async_trait] impl crate::Reloadable for CtrlPower { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(mut config) = self.config.try_lock() { + async fn reload(&mut self) -> Result<(), RogError> { + if let Some(mut config) = self.config.try_lock() { config.read(); self.set(config.bat_charge_limit)?; } @@ -96,7 +129,7 @@ impl CtrlPower { info!("Battery charge limit: {}", limit); - if let Ok(mut config) = self.config.try_lock() { + if let Some(mut config) = self.config.try_lock() { config.read(); config.bat_charge_limit = limit; config.write(); @@ -104,19 +137,24 @@ impl CtrlPower { Ok(()) } + + task_watch_item!(charge_control_end_threshold power); } #[async_trait] impl CtrlTask for CtrlPower { - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { + fn zbus_path() -> &'static str { + ZBUS_PATH + } + + async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { let power1 = self.clone(); let power2 = self.clone(); self.create_sys_event_tasks( - executor, move || {}, move || { info!("CtrlCharge reloading charge limit"); - if let Ok(lock) = power1.config.try_lock() { + if let Some(lock) = power1.config.try_lock() { power1 .set(lock.bat_charge_limit) .map_err(|err| { @@ -129,7 +167,7 @@ impl CtrlTask for CtrlPower { move || {}, move || { info!("CtrlCharge reloading charge limit"); - if let Ok(lock) = power2.config.try_lock() { + if let Some(lock) = power2.config.try_lock() { power2 .set(lock.bat_charge_limit) .map_err(|err| { @@ -142,6 +180,30 @@ impl CtrlTask for CtrlPower { ) .await; + self.watch_charge_control_end_threshold(signal_ctxt.clone()) + .await?; + + let ctrl = self.clone(); + match ctrl.power.monitor_online() { + Ok(mut watch) => { + tokio::spawn(async move { + let mut buffer = [0; 32]; + watch + .event_stream(&mut buffer) + .unwrap() + .for_each(|_| async { + if let Ok(value) = ctrl.power.get_online() { + Self::notify_mains_online(&signal_ctxt, value == 1) + .await + .unwrap(); + } + }) + .await; + }); + } + Err(e) => info!("inotify watch failed: {}", e), + } + Ok(()) } } diff --git a/daemon/src/ctrl_profiles/controller.rs b/daemon/src/ctrl_profiles/controller.rs index 80b9a60d..331a1458 100644 --- a/daemon/src/ctrl_profiles/controller.rs +++ b/daemon/src/ctrl_profiles/controller.rs @@ -1,17 +1,16 @@ use crate::error::RogError; -use crate::{CtrlTask, GetSupported}; -use async_trait::async_trait; +use crate::GetSupported; use log::{info, warn}; +use rog_platform::platform::AsusPlatform; use rog_platform::supported::PlatformProfileFunctions; use rog_profiles::error::ProfileError; use rog_profiles::{FanCurveProfiles, Profile}; -use smol::Executor; -use std::sync::{Arc, Mutex}; use super::config::ProfileConfig; pub struct CtrlPlatformProfile { pub config: ProfileConfig, + pub platform: AsusPlatform, } impl GetSupported for CtrlPlatformProfile { @@ -39,27 +38,13 @@ impl GetSupported for CtrlPlatformProfile { } } -impl crate::Reloadable for CtrlPlatformProfile { - /// Fetch the active profile and use that to set all related components up - fn reload(&mut self) -> Result<(), RogError> { - if let Some(curves) = &mut self.config.fan_curves { - if let Ok(mut device) = FanCurveProfiles::get_device() { - // There is a possibility that the curve was default zeroed, so this call initialises - // the data from system read and we need to save it after - curves.write_profile_curve_to_platform(self.config.active_profile, &mut device)?; - self.config.write(); - } - } - Ok(()) - } -} - impl CtrlPlatformProfile { pub fn new(config: ProfileConfig) -> Result { - if Profile::is_platform_profile_supported() { + let platform = AsusPlatform::new()?; + if platform.has_platform_profile() || platform.has_throttle_thermal_policy() { info!("Device has profile control available"); - let mut controller = CtrlPlatformProfile { config }; + let mut controller = CtrlPlatformProfile { config, platform }; if FanCurveProfiles::get_device().is_ok() { info!("Device has fan curves available"); if controller.config.fan_curves.is_none() { @@ -129,32 +114,3 @@ impl CtrlPlatformProfile { Ok(()) } } - -pub struct CtrlProfileTask { - ctrl: Arc>, -} - -impl CtrlProfileTask { - pub fn new(ctrl: Arc>) -> Self { - Self { ctrl } - } -} - -#[async_trait] -impl CtrlTask for CtrlProfileTask { - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - let ctrl = self.ctrl.clone(); - self.repeating_task(666, executor, move || { - if let Ok(ref mut lock) = ctrl.try_lock() { - let new_profile = Profile::get_active_profile().unwrap(); - if new_profile != lock.config.active_profile { - lock.config.active_profile = new_profile; - lock.write_profile_curve_to_platform().unwrap(); - lock.save_config(); - } - } - }) - .await; - Ok(()) - } -} diff --git a/daemon/src/ctrl_profiles/mod.rs b/daemon/src/ctrl_profiles/mod.rs index 85a4d229..9c365c20 100644 --- a/daemon/src/ctrl_profiles/mod.rs +++ b/daemon/src/ctrl_profiles/mod.rs @@ -1,3 +1,4 @@ pub mod config; pub mod controller; -pub mod zbus; +/// Implements CtrlTask, Reloadable, ZbusRun +pub mod trait_impls; diff --git a/daemon/src/ctrl_profiles/trait_impls.rs b/daemon/src/ctrl_profiles/trait_impls.rs new file mode 100644 index 00000000..b8f1f8eb --- /dev/null +++ b/daemon/src/ctrl_profiles/trait_impls.rs @@ -0,0 +1,247 @@ +use async_trait::async_trait; +use log::warn; +use rog_profiles::fan_curve_set::CurveData; +use rog_profiles::fan_curve_set::FanCurveSet; +use rog_profiles::FanCurveProfiles; +use rog_profiles::Profile; +use zbus::export::futures_util::lock::Mutex; +use zbus::export::futures_util::StreamExt; +use zbus::Connection; +use zbus::SignalContext; + +use std::sync::Arc; +use zbus::{dbus_interface, fdo::Error}; + +use crate::error::RogError; +use crate::CtrlTask; + +use super::controller::CtrlPlatformProfile; + +const ZBUS_PATH: &str = "/org/asuslinux/Profile"; +const UNSUPPORTED_MSG: &str = + "Fan curves are not supported on this laptop or you require a patched kernel"; + +#[derive(Clone)] +pub struct ProfileZbus(pub Arc>); + +#[dbus_interface(name = "org.asuslinux.Daemon")] +impl ProfileZbus { + /// Fetch profile names + fn profiles(&mut self) -> zbus::fdo::Result> { + if let Ok(profiles) = Profile::get_profile_names() { + return Ok(profiles); + } + Err(Error::Failed( + "Failed to get all profile details".to_string(), + )) + } + + /// Toggle to next platform_profile. Names provided by `Profiles`. + /// If fan-curves are supported will also activate a fan curve for profile. + async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) { + let mut ctrl = self.0.lock().await; + ctrl.set_next_profile() + .unwrap_or_else(|err| warn!("{}", err)); + ctrl.save_config(); + + Self::notify_profile(&ctxt, ctrl.config.active_profile) + .await + .ok(); + } + + /// Fetch the active profile name + async fn active_profile(&mut self) -> zbus::fdo::Result { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + Ok(ctrl.config.active_profile) + } + + /// Set this platform_profile name as active + async fn set_active_profile( + &self, + #[zbus(signal_context)] ctxt: SignalContext<'_>, + profile: Profile, + ) { + let mut ctrl = self.0.lock().await; + // Read first just incase the user has modified the config before calling this + ctrl.config.read(); + Profile::set_profile(profile) + .map_err(|e| warn!("set_profile, {}", e)) + .ok(); + ctrl.config.active_profile = profile; + ctrl.write_profile_curve_to_platform() + .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) + .ok(); + + ctrl.save_config(); + + Self::notify_profile(&ctxt, ctrl.config.active_profile) + .await + .ok(); + } + + /// Get a list of profiles that have fan-curves enabled. + async fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + if let Some(curves) = &ctrl.config.fan_curves { + return Ok(curves.get_enabled_curve_profiles().to_vec()); + } + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); + } + + /// Set a profile fan curve enabled status. Will also activate a fan curve if in the + /// same profile mode + async fn set_fan_curve_enabled( + &mut self, + profile: Profile, + enabled: bool, + ) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + return if let Some(curves) = &mut ctrl.config.fan_curves { + curves.set_profile_curve_enabled(profile, enabled); + + ctrl.write_profile_curve_to_platform() + .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) + .ok(); + + ctrl.save_config(); + Ok(()) + } else { + Err(Error::Failed(UNSUPPORTED_MSG.to_string())) + }; + } + + /// Get the fan-curve data for the currently active Profile + async fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + if let Some(curves) = &ctrl.config.fan_curves { + let curve = curves.get_fan_curves_for(profile); + return Ok(curve.clone()); + } + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); + } + + /// Set the fan curve for the specified profile. + /// Will also activate the fan curve if the user is in the same mode. + async fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + if let Some(curves) = &mut ctrl.config.fan_curves { + curves + .save_fan_curve(curve, profile) + .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; + } else { + return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); + } + ctrl.write_profile_curve_to_platform() + .map_err(|e| warn!("Profile::set_profile, {}", e)) + .ok(); + ctrl.save_config(); + + Ok(()) + } + + /// Reset the stored (self) and device curve to the defaults of the platform. + /// + /// Each platform_profile has a different default and the defualt can be read + /// only for the currently active profile. + async fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + ctrl.set_active_curve_to_defaults() + .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) + .ok(); + ctrl.save_config(); + Ok(()) + } + + /// Reset the stored (self) and device curve to the defaults of the platform. + /// + /// Each platform_profile has a different default and the defualt can be read + /// only for the currently active profile. + async fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> { + let mut ctrl = self.0.lock().await; + ctrl.config.read(); + let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); + + Profile::set_profile(profile) + .map_err(|e| warn!("set_profile, {}", e)) + .ok(); + ctrl.set_active_curve_to_defaults() + .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) + .ok(); + + Profile::set_profile(active) + .map_err(|e| warn!("set_profile, {}", e)) + .ok(); + ctrl.save_config(); + Ok(()) + } + + #[dbus_interface(signal)] + async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> { + } +} + +#[async_trait] +impl crate::ZbusRun for ProfileZbus { + async fn add_to_server(self, server: &mut Connection) { + Self::add_to_server_helper(self, ZBUS_PATH, server).await; + } +} + +#[async_trait] +impl CtrlTask for ProfileZbus { + fn zbus_path() -> &'static str { + ZBUS_PATH + } + + async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { + let ctrl = self.0.clone(); + let mut watch = self.0.lock().await.platform.monitor_platform_profile()?; + + tokio::spawn(async move { + let mut buffer = [0; 32]; + watch + .event_stream(&mut buffer) + .unwrap() + .for_each(|_| async { + let mut lock = ctrl.lock().await; + let new_profile = Profile::get_active_profile().unwrap(); + if new_profile != lock.config.active_profile { + lock.config.active_profile = new_profile; + lock.write_profile_curve_to_platform().unwrap(); + lock.save_config(); + } + + Self::notify_profile(&signal_ctxt.clone(), lock.config.active_profile) + .await + .ok(); + }) + .await; + }); + + Ok(()) + } +} + +#[async_trait] +impl crate::Reloadable for ProfileZbus { + /// Fetch the active profile and use that to set all related components up + async fn reload(&mut self) -> Result<(), RogError> { + let mut ctrl = self.0.lock().await; + let active = ctrl.config.active_profile; + if let Some(curves) = &mut ctrl.config.fan_curves { + if let Ok(mut device) = FanCurveProfiles::get_device() { + // There is a possibility that the curve was default zeroed, so this call initialises + // the data from system read and we need to save it after + curves.write_profile_curve_to_platform(active, &mut device)?; + ctrl.config.write(); + } + } + Ok(()) + } +} diff --git a/daemon/src/ctrl_profiles/zbus.rs b/daemon/src/ctrl_profiles/zbus.rs deleted file mode 100644 index 91fdcd08..00000000 --- a/daemon/src/ctrl_profiles/zbus.rs +++ /dev/null @@ -1,211 +0,0 @@ -use async_trait::async_trait; -use log::warn; -use rog_profiles::fan_curve_set::CurveData; -use rog_profiles::fan_curve_set::FanCurveSet; -use rog_profiles::Profile; -use zbus::Connection; -use zbus::SignalContext; - -use std::sync::Arc; -use std::sync::Mutex; -use zbus::{dbus_interface, fdo::Error}; - -use super::controller::CtrlPlatformProfile; - -static UNSUPPORTED_MSG: &str = - "Fan curves are not supported on this laptop or you require a patched kernel"; - -pub struct ProfileZbus { - inner: Arc>, -} - -impl ProfileZbus { - pub fn new(inner: Arc>) -> Self { - Self { inner } - } -} - -#[dbus_interface(name = "org.asuslinux.Daemon")] -impl ProfileZbus { - /// Fetch profile names - fn profiles(&mut self) -> zbus::fdo::Result> { - if let Ok(profiles) = Profile::get_profile_names() { - return Ok(profiles); - } - Err(Error::Failed( - "Failed to get all profile details".to_string(), - )) - } - - /// Toggle to next platform_profile. Names provided by `Profiles`. - /// If fan-curves are supported will also activate a fan curve for profile. - async fn next_profile(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) { - let mut profile = None; - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.set_next_profile() - .unwrap_or_else(|err| warn!("{}", err)); - ctrl.save_config(); - profile = Some(ctrl.config.active_profile); - } - if let Some(profile) = profile { - Self::notify_profile(&ctxt, profile).await.ok(); - } - } - - /// Fetch the active profile name - fn active_profile(&mut self) -> zbus::fdo::Result { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - return Ok(ctrl.config.active_profile); - } - Err(Error::Failed( - "Failed to get active profile name".to_string(), - )) - } - - /// Set this platform_profile name as active - async fn set_active_profile( - &self, - #[zbus(signal_context)] ctxt: SignalContext<'_>, - profile: Profile, - ) { - let mut tmp = None; - if let Ok(mut ctrl) = self.inner.try_lock() { - // Read first just incase the user has modified the config before calling this - ctrl.config.read(); - Profile::set_profile(profile) - .map_err(|e| warn!("set_profile, {}", e)) - .ok(); - ctrl.config.active_profile = profile; - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) - .ok(); - - ctrl.save_config(); - tmp = Some(ctrl.config.active_profile); - } - if let Some(profile) = tmp { - Self::notify_profile(&ctxt, profile).await.ok(); - } - } - - /// Get a list of profiles that have fan-curves enabled. - fn enabled_fan_profiles(&mut self) -> zbus::fdo::Result> { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - if let Some(curves) = &ctrl.config.fan_curves { - return Ok(curves.get_enabled_curve_profiles().to_vec()); - } - return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); - } - Err(Error::Failed( - "Failed to get enabled fan curve names".to_string(), - )) - } - - /// Set a profile fan curve enabled status. Will also activate a fan curve if in the - /// same profile mode - fn set_fan_curve_enabled(&mut self, profile: Profile, enabled: bool) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - return if let Some(curves) = &mut ctrl.config.fan_curves { - curves.set_profile_curve_enabled(profile, enabled); - - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("write_profile_curve_to_platform, {}", e)) - .ok(); - - ctrl.save_config(); - Ok(()) - } else { - Err(Error::Failed(UNSUPPORTED_MSG.to_string())) - }; - } - Err(Error::Failed( - "Failed to get enabled fan curve names".to_string(), - )) - } - - /// Get the fan-curve data for the currently active Profile - fn fan_curve_data(&mut self, profile: Profile) -> zbus::fdo::Result { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - if let Some(curves) = &ctrl.config.fan_curves { - let curve = curves.get_fan_curves_for(profile); - return Ok(curve.clone()); - } - return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); - } - Err(Error::Failed("Failed to get fan curve data".to_string())) - } - - /// Set the fan curve for the specified profile. - /// Will also activate the fan curve if the user is in the same mode. - fn set_fan_curve(&self, profile: Profile, curve: CurveData) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - if let Some(curves) = &mut ctrl.config.fan_curves { - curves - .save_fan_curve(curve, profile) - .map_err(|err| zbus::fdo::Error::Failed(err.to_string()))?; - } else { - return Err(Error::Failed(UNSUPPORTED_MSG.to_string())); - } - ctrl.write_profile_curve_to_platform() - .map_err(|e| warn!("Profile::set_profile, {}", e)) - .ok(); - ctrl.save_config(); - } - Ok(()) - } - - /// Reset the stored (self) and device curve to the defaults of the platform. - /// - /// Each platform_profile has a different default and the defualt can be read - /// only for the currently active profile. - fn set_active_curve_to_defaults(&self) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) - .ok(); - ctrl.save_config(); - } - Ok(()) - } - - /// Reset the stored (self) and device curve to the defaults of the platform. - /// - /// Each platform_profile has a different default and the defualt can be read - /// only for the currently active profile. - fn reset_profile_curves(&self, profile: Profile) -> zbus::fdo::Result<()> { - if let Ok(mut ctrl) = self.inner.try_lock() { - ctrl.config.read(); - let active = Profile::get_active_profile().unwrap_or(Profile::Balanced); - - Profile::set_profile(profile) - .map_err(|e| warn!("set_profile, {}", e)) - .ok(); - ctrl.set_active_curve_to_defaults() - .map_err(|e| warn!("Profile::set_active_curve_to_defaults, {}", e)) - .ok(); - - Profile::set_profile(active) - .map_err(|e| warn!("set_profile, {}", e)) - .ok(); - ctrl.save_config(); - } - Ok(()) - } - - #[dbus_interface(signal)] - async fn notify_profile(signal_ctxt: &SignalContext<'_>, profile: Profile) -> zbus::Result<()> { - } -} - -#[async_trait] -impl crate::ZbusAdd for ProfileZbus { - async fn add_to_server(self, server: &mut Connection) { - Self::add_to_server_helper(self, "/org/asuslinux/Profile", server).await; - } -} diff --git a/daemon/src/ctrl_supported.rs b/daemon/src/ctrl_supported.rs index 4c4bec3d..993d8271 100644 --- a/daemon/src/ctrl_supported.rs +++ b/daemon/src/ctrl_supported.rs @@ -5,7 +5,7 @@ use zbus::Connection; use zvariant::Type; use crate::{ - ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlRogBios, + ctrl_anime::CtrlAnime, ctrl_aura::controller::CtrlKbdLed, ctrl_platform::CtrlPlatform, ctrl_power::CtrlPower, ctrl_profiles::controller::CtrlPlatformProfile, GetSupported, }; @@ -28,7 +28,7 @@ impl SupportedFunctions { } #[async_trait] -impl crate::ZbusAdd for SupportedFunctions { +impl crate::ZbusRun for SupportedFunctions { async fn add_to_server(self, server: &mut Connection) { Self::add_to_server_helper(self, "/org/asuslinux/Supported", server).await; } @@ -43,7 +43,7 @@ impl GetSupported for SupportedFunctions { keyboard_led: CtrlKbdLed::get_supported(), charge_ctrl: CtrlPower::get_supported(), platform_profile: CtrlPlatformProfile::get_supported(), - rog_bios_ctrl: CtrlRogBios::get_supported(), + rog_bios_ctrl: CtrlPlatform::get_supported(), } } } diff --git a/daemon/src/daemon.rs b/daemon/src/daemon.rs index d0a39896..b9ced919 100644 --- a/daemon/src/daemon.rs +++ b/daemon/src/daemon.rs @@ -1,38 +1,35 @@ use std::env; use std::error::Error; use std::io::Write; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +use std::time::Duration; +use ::zbus::export::futures_util::lock::Mutex; use ::zbus::Connection; -use daemon::ctrl_profiles::controller::CtrlProfileTask; +use daemon::ctrl_anime::CtrlAnime; use log::LevelFilter; use log::{error, info, warn}; -use smol::Executor; +use tokio::time::sleep; -use daemon::ctrl_anime::config::AnimeConfig; -use daemon::ctrl_anime::zbus::CtrlAnimeZbus; -use daemon::ctrl_anime::*; -use daemon::ctrl_aura::config::AuraConfig; -use daemon::ctrl_aura::controller::{ - CtrlKbdLed, CtrlKbdLedReloader, CtrlKbdLedTask, CtrlKbdLedZbus, -}; -use daemon::ctrl_platform::CtrlRogBios; +use daemon::ctrl_anime::{config::AnimeConfig, trait_impls::CtrlAnimeZbus}; +use daemon::ctrl_aura::{config::AuraConfig, controller::CtrlKbdLed, trait_impls::CtrlKbdLedZbus}; +use daemon::ctrl_platform::CtrlPlatform; use daemon::ctrl_power::CtrlPower; -use daemon::ctrl_profiles::config::ProfileConfig; +use daemon::ctrl_profiles::{ + config::ProfileConfig, controller::CtrlPlatformProfile, trait_impls::ProfileZbus, +}; +use daemon::laptops::LaptopLedData; use daemon::{ config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported, }; -use daemon::{ - ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus}, - laptops::LaptopLedData, -}; -use daemon::{CtrlTask, Reloadable, ZbusAdd}; +use daemon::{CtrlTask, Reloadable, ZbusRun}; use rog_dbus::DBUS_NAME; use rog_profiles::Profile; static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf"; -pub fn main() -> Result<(), Box> { +#[tokio::main] +async fn main() -> Result<(), Box> { let mut logger = env_logger::Builder::new(); logger .target(env_logger::Target::Stdout) @@ -61,14 +58,12 @@ pub fn main() -> Result<(), Box> { info!(" rog-profiles v{}", rog_profiles::VERSION); info!("rog-platform v{}", rog_platform::VERSION); - let mut executor = Executor::new(); - - smol::block_on(start_daemon(&mut executor))?; + start_daemon().await?; Ok(()) } /// The actual main loop for the daemon -async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> { +async fn start_daemon() -> Result<(), Box> { let supported = SupportedFunctions::get_supported(); print_board_info(); println!("{}", serde_json::to_string_pretty(&supported)?); @@ -81,77 +76,46 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> supported.add_to_server(&mut connection).await; - match CtrlRogBios::new(config.clone()) { - Ok(mut ctrl) => { - // Do a reload of any settings - ctrl.reload() - .unwrap_or_else(|err| warn!("CtrlRogBios: {}", err)); - // Then register to dbus server - ctrl.add_to_server(&mut connection).await; - - let task = CtrlRogBios::new(config.clone())?; - task.create_tasks(executor).await.ok(); + match CtrlPlatform::new(config.clone()) { + Ok(ctrl) => { + start_tasks(ctrl, &mut connection).await?; } Err(err) => { - error!("rog_bios_control: {}", err); + error!("CtrlPlatform: {}", err); } } match CtrlPower::new(config.clone()) { - Ok(mut ctrl) => { - // Do a reload of any settings - ctrl.reload() - .unwrap_or_else(|err| warn!("CtrlPower: {}", err)); - // Then register to dbus server - ctrl.add_to_server(&mut connection).await; - - let task = CtrlPower::new(config)?; - task.create_tasks(executor).await.ok(); + Ok(ctrl) => { + start_tasks(ctrl, &mut connection).await?; } Err(err) => { - error!("charge_control: {}", err); + error!("CtrlPower: {}", err); } } if Profile::is_platform_profile_supported() { let profile_config = ProfileConfig::load(PROFILE_CONFIG_PATH.into()); match CtrlPlatformProfile::new(profile_config) { - Ok(mut ctrl) => { - ctrl.reload() - .unwrap_or_else(|err| warn!("Profile control: {}", err)); - - let tmp = Arc::new(Mutex::new(ctrl)); - let task = CtrlProfileTask::new(tmp.clone()); - task.create_tasks(executor).await.ok(); - - let task = ProfileZbus::new(tmp.clone()); - task.add_to_server(&mut connection).await; + Ok(ctrl) => { + let zbus = ProfileZbus(Arc::new(Mutex::new(ctrl))); + start_tasks(zbus, &mut connection).await?; } Err(err) => { error!("Profile control: {}", err); } } } else { - warn!("platform_profile support not found. This requires kernel 5.15.x or the patch applied: https://lkml.org/lkml/2021/8/18/1022"); + warn!("platform_profile support not found"); } match CtrlAnime::new(AnimeConfig::load()) { Ok(ctrl) => { - let inner = Arc::new(Mutex::new(ctrl)); - - let mut reload = CtrlAnimeReloader(inner.clone()); - reload - .reload() - .unwrap_or_else(|err| warn!("AniMe: {}", err)); - - let zbus = CtrlAnimeZbus(inner.clone()); - zbus.add_to_server(&mut connection).await; - - let task = CtrlAnimeTask::new(inner).await; - task.create_tasks(executor).await.ok(); + let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl))); + start_tasks(zbus, &mut connection).await?; } Err(err) => { - error!("AniMe control: {}", err); + info!("AniMe control: {}", err); } } @@ -159,19 +123,8 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> let aura_config = AuraConfig::load(&laptop); match CtrlKbdLed::new(laptop, aura_config) { Ok(ctrl) => { - let inner = Arc::new(Mutex::new(ctrl)); - - let mut reload = CtrlKbdLedReloader(inner.clone()); - reload - .reload() - .unwrap_or_else(|err| warn!("Keyboard LED control: {}", err)); - - CtrlKbdLedZbus::new(inner.clone()) - .add_to_server(&mut connection) - .await; - - let task = CtrlKbdLedTask::new(inner); - task.create_tasks(executor).await.ok(); + let zbus = CtrlKbdLedZbus(Arc::new(Mutex::new(ctrl))); + start_tasks(zbus, &mut connection).await?; } Err(err) => { error!("Keyboard control: {}", err); @@ -180,7 +133,26 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box> // Request dbus name after finishing initalizing all functions connection.request_name(DBUS_NAME).await?; + loop { - smol::block_on(executor.tick()); + // This is just a blocker to idle and ensure the reator reacts + sleep(Duration::from_millis(1000)).await; } } + +async fn start_tasks(mut zbus: T, connection: &mut Connection) -> Result<(), Box> +where + T: ZbusRun + Reloadable + CtrlTask + Clone, +{ + let task = zbus.clone(); + + zbus.reload() + .await + .unwrap_or_else(|err| warn!("Controller error: {}", err)); + zbus.add_to_server(connection).await; + + task.create_tasks(CtrlKbdLedZbus::signal_context(&connection)?) + .await + .ok(); + Ok(()) +} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 1009f8be..281e8d3c 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -19,25 +19,73 @@ pub mod ctrl_supported; pub mod error; -use std::time::Duration; - use crate::error::RogError; use async_trait::async_trait; -use config::Config; use log::warn; use logind_zbus::manager::ManagerProxy; -use smol::{stream::StreamExt, Executor, Timer}; -use zbus::Connection; +use zbus::{export::futures_util::StreamExt, Connection, SignalContext}; use zvariant::ObjectPath; +/// This macro adds a function which spawns an `inotify` task on the passed in `Executor`. +/// +/// The generated function is `watch_()`. Self requires the following methods to be available: +/// - `() -> SomeValue`, functionally is a getter, but is allowed to have side effects. +/// - `notify_(SignalContext, SomeValue)` +/// +/// In most cases if `SomeValue` is stored in a config then `()` getter is expected to update it. +/// The getter should *never* write back to the path or attribute that is being watched or an +/// infinite loop will occur. +/// +/// # Example +/// +/// ```ignore +/// impl CtrlRogBios { +/// task_watch_item!(panel_od platform); +/// task_watch_item!(gpu_mux_mode platform); +/// } +/// ``` +#[macro_export] +macro_rules! task_watch_item { + ($name:ident $self_inner:ident) => { + concat_idents::concat_idents!(fn_name = watch_, $name { + async fn fn_name( + &self, + signal_ctxt: SignalContext<'static>, + ) -> Result<(), RogError> { + use zbus::export::futures_util::StreamExt; + + let ctrl = self.clone(); + concat_idents::concat_idents!(watch_fn = monitor_, $name { + match self.$self_inner.watch_fn() { + Ok(mut watch) => { + tokio::spawn(async move { + let mut buffer = [0; 32]; + watch.event_stream(&mut buffer).unwrap().for_each(|_| async { + let value = ctrl.$name(); + concat_idents::concat_idents!(notif_fn = notify_, $name { + Self::notif_fn(&signal_ctxt, value).await.unwrap(); + }); + }).await; + }); + } + Err(e) => info!("inotify watch failed: {}. You can ignore this if your device does not support the feature", e), + } + }); + Ok(()) + } + }); + }; +} + pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +#[async_trait] pub trait Reloadable { - fn reload(&mut self) -> Result<(), RogError>; + async fn reload(&mut self) -> Result<(), RogError>; } #[async_trait] -pub trait ZbusAdd { +pub trait ZbusRun { async fn add_to_server(self, server: &mut Connection); async fn add_to_server_helper( @@ -60,29 +108,33 @@ pub trait ZbusAdd { /// Set up a task to run on the async executor #[async_trait] pub trait CtrlTask { - /// Implement to set up various tasks that may be required, using the `Executor`. - /// No blocking loops are allowed, or they must be run on a separate thread. - async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError>; + fn zbus_path() -> &'static str; - /// Create a timed repeating task - async fn repeating_task( - &self, - millis: u64, - executor: &mut Executor, - mut task: impl FnMut() + Send + 'static, - ) { - let timer = Timer::interval(Duration::from_millis(millis)); - executor - .spawn(async move { - timer.for_each(|_| task()).await; - }) - .detach(); + fn signal_context(connection: &Connection) -> Result, zbus::Error> { + SignalContext::new(connection, Self::zbus_path()) } + /// Implement to set up various tasks that may be required, using the `Executor`. + /// No blocking loops are allowed, or they must be run on a separate thread. + async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>; + + // /// Create a timed repeating task + // async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send + 'static) { + // use std::time::Duration; + // use tokio::time; + // let mut timer = time::interval(Duration::from_millis(millis)); + // tokio::spawn(async move { + // timer.tick().await; + // task(); + // }); + // } + /// Free helper method to create tasks to run on: sleep, wake, shutdown, boot + /// + /// The closures can potentially block, so execution time should be the minimal possible + /// such as save a variable. async fn create_sys_event_tasks( &self, - executor: &mut Executor, mut on_sleep: impl FnMut() + Send + 'static, mut on_wake: impl FnMut() + Send + 'static, mut on_shutdown: impl FnMut() + Send + 'static, @@ -96,54 +148,40 @@ pub trait CtrlTask { .await .expect("Controller could not create ManagerProxy"); - executor - .spawn(async move { - if let Ok(notif) = manager.receive_prepare_for_sleep().await { - notif - .for_each(|event| { - if let Ok(args) = event.args() { - if args.start { - on_sleep(); - } else if !args.start() { - on_wake(); - } - } - }) - .await; + tokio::spawn(async move { + if let Ok(mut notif) = manager.receive_prepare_for_sleep().await { + while let Some(event) = notif.next().await { + if let Ok(args) = event.args() { + if args.start { + on_sleep(); + } else if !args.start() { + on_wake(); + } + } } - }) - .detach(); + } + }); let manager = ManagerProxy::new(&connection) .await .expect("Controller could not create ManagerProxy"); - executor - .spawn(async move { - if let Ok(notif) = manager.receive_prepare_for_shutdown().await { - notif - .for_each(|event| { - if let Ok(args) = event.args() { - if args.start { - on_shutdown(); - } else if !args.start() { - on_boot(); - } - } - }) - .await; + tokio::spawn(async move { + if let Ok(mut notif) = manager.receive_prepare_for_shutdown().await { + while let Some(event) = notif.next().await { + if let Ok(args) = event.args() { + if args.start { + on_shutdown(); + } else if !args.start() { + on_boot(); + } + } } - }) - .detach(); + } + }); } } -pub trait CtrlTaskComplex { - type A; - - fn do_task(&mut self, config: &mut Config, event: Self::A); -} - pub trait GetSupported { type A; diff --git a/data/asusd-ledmodes.toml b/data/asusd-ledmodes.toml index 46c4de80..8e625a62 100644 --- a/data/asusd-ledmodes.toml +++ b/data/asusd-ledmodes.toml @@ -49,7 +49,7 @@ per_key = false [[led_data]] prod_family = "ROG Zephyrus M16" -board_names = ["GU603HR", "GU603HE", "GU603HM"] +board_names = ["GU603Z", "GU603H"] standard = ["Static", "Breathe", "Strobe", "Rainbow", "Pulse"] multizone = [] per_key = false diff --git a/design-patterns.md b/design-patterns.md index 25b59b52..abe6f135 100644 --- a/design-patterns.md +++ b/design-patterns.md @@ -18,81 +18,112 @@ Then for each trait that is required a new struct is required that can have the Main controller: +For a very simple controller that doesn't need exclusive access you can clone across threads + +```rust +#[derive(Clone)] +pub struct CtrlAnime { + + config: Arc>, +} + +// This is the task trait used for such things as file watches, or logind +// notifications (boot/suspend/shutdown etc) +impl crate::CtrlTask for CtrlAnime {} + +// The trait to easily add the controller to Zbus to enable the zbus derived functions +// to be polled, run, react etc. +impl crate::ZbusAdd for CtrlAnime {} + +impl CtrlAnime {} +``` + + Otherwise, you will need to share the controller via mutex + ```rust pub struct CtrlAnime { } +// Like this +#[derive(Clone)] +pub struct CtrlAnimeTask(Arc>); -impl CtrlAnime { - -} +#[derive(Clone)] +pub struct CtrlAnimeZbus(Arc>); + +impl CtrlAnime {} ``` -The task trait. There are three ways to implement this: +The task trait: ```rust +// Mutex should always be async mutex pub struct CtrlAnimeTask(Arc>); impl crate::CtrlTask for CtrlAnimeTask { // This will run once only - fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - if let Ok(lock) = self.inner.try_lock() { - - } + async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { + let lock self.inner.lock().await; + Ok(()) } // This will run until the notification stream closes (which in most cases will be never) - fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - let connection = Connection::system().await.unwrap(); - let manager = ManagerProxy::new(&connection).await.unwrap(); - - let inner = self.inner.clone(); - executor - .spawn(async move { - // A notification from logind dbus interface - if let Ok(p) = manager.receive_prepare_for_sleep().await { - // A stream that will continuously output events - p.for_each(|_| { - if let Ok(lock) = inner.try_lock() { - // Do stuff here - } - }) - .await; + async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> { + let inner1 = self.inner.clone(); + let inner2 = self.inner.clone(); + let inner3 = self.inner.clone(); + let inner4 = self.inner.clone(); + // This is a free method on CtrlTask trait + self.create_sys_event_tasks( + // Loop is required to try an attempt to get the mutex *without* blocking + // other threads - it is possible to end up with deadlocks otherwise. + move || loop { + if let Some(lock) = inner1.try_lock() { + run_action(true, lock, inner1.clone()); + break; } - }) - .detach(); - } - - // This task will run every 500 milliseconds - fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> { - let inner = self.inner.clone(); - // This is a provided free trait to help set up a repeating task - self.repeating_task(500, executor, move || { - if let Ok(lock) = inner.try_lock() { - // Do stuff here - } - }) + }, + move || loop { + if let Some(lock) = inner2.try_lock() { + run_action(false, lock, inner2.clone()); + break; + } + }, + move || loop { + if let Some(lock) = inner3.try_lock() { + run_action(true, lock, inner3.clone()); + break; + } + }, + move || loop { + if let Some(lock) = inner4.try_lock() { + run_action(false, lock, inner4.clone()); + break; + } + }, + ) .await; } } ``` The reloader trait + ```rust pub struct CtrlAnimeReloader(Arc>); impl crate::Reloadable for CtrlAnimeReloader { - fn reload(&mut self) -> Result<(), RogError> { - if let Ok(lock) = self.inner.try_lock() { - - } + async fn reload(&mut self) -> Result<(), RogError> { + let lock = self.inner.lock().await; + Ok(()) } } ``` The Zbus requirements: + ```rust pub struct CtrlAnimeZbus(Arc>); @@ -106,10 +137,9 @@ impl crate::ZbusAdd for CtrlAnimeZbus { #[dbus_interface(name = "org.asuslinux.Daemon")] impl CtrlAnimeZbus { - fn () { - if let Ok(lock) = self.inner.try_lock() { - - } + async fn () { + let lock = self.inner.lock().await; + } } ``` diff --git a/rog-control-center/src/main.rs b/rog-control-center/src/main.rs index 634342aa..c5bf8b46 100644 --- a/rog-control-center/src/main.rs +++ b/rog-control-center/src/main.rs @@ -156,7 +156,6 @@ fn main() -> Result<(), Box> { start_closed = false; continue; } - dbg!("asda"); } Ok(()) } diff --git a/rog-control-center/src/mocking.rs b/rog-control-center/src/mocking.rs index f6cd60b8..7291c44c 100644 --- a/rog-control-center/src/mocking.rs +++ b/rog-control-center/src/mocking.rs @@ -65,7 +65,7 @@ impl Bios { pub fn gpu_mux_mode(&self) -> Result { Ok(1) } - pub fn panel_overdrive(&self) -> Result { + pub fn panel_od(&self) -> Result { Ok(1) } pub fn set_post_boot_sound(&self, _b: bool) -> Result<()> { @@ -74,7 +74,7 @@ impl Bios { pub fn set_gpu_mux_mode(&self, _b: bool) -> Result<()> { Ok(()) } - pub fn set_panel_overdrive(&self, _b: bool) -> Result<()> { + pub fn set_panel_od(&self, _b: bool) -> Result<()> { Ok(()) } } @@ -217,7 +217,7 @@ impl Supported { rog_bios_ctrl: RogBiosSupportedFunctions { post_sound: true, dedicated_gfx: true, - panel_overdrive: true, + panel_od: true, dgpu_disable: true, egpu_enable: true, }, diff --git a/rog-control-center/src/notify.rs b/rog-control-center/src/notify.rs index 2012ba96..a8cb83f7 100644 --- a/rog-control-center/src/notify.rs +++ b/rog-control-center/src/notify.rs @@ -4,8 +4,8 @@ use notify_rust::{Hint, Notification, NotificationHandle}; use rog_aura::AuraEffect; use rog_dbus::{ - zbus_anime::AnimeProxy, zbus_charge::ChargeProxy, zbus_led::LedProxy, - zbus_platform::RogBiosProxy, zbus_profile::ProfileProxy, + zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy, + zbus_power::PowerProxy, zbus_profile::ProfileProxy, }; use rog_profiles::Profile; use smol::{future, Executor}; @@ -70,7 +70,7 @@ pub fn start_notifications( if let Ok(out) = e.args() { if notifs_enabled1.load(Ordering::SeqCst) { if let Ok(ref mut lock) = last_notif.try_lock() { - notify!(do_post_sound_notif, lock, &out.sound()); + notify!(do_post_sound_notif, lock, &out.on()); } } bios_notified1.store(true, Ordering::SeqCst); @@ -86,7 +86,7 @@ pub fn start_notifications( .spawn(async move { let conn = zbus::Connection::system().await.unwrap(); let proxy = RogBiosProxy::new(&conn).await.unwrap(); - if let Ok(p) = proxy.receive_notify_panel_overdrive().await { + if let Ok(p) = proxy.receive_notify_panel_od().await { p.for_each(|_| { bios_notified.store(true, Ordering::SeqCst); future::ready(()) @@ -102,8 +102,8 @@ pub fn start_notifications( executor .spawn(async move { let conn = zbus::Connection::system().await.unwrap(); - let proxy = ChargeProxy::new(&conn).await.unwrap(); - if let Ok(p) = proxy.receive_notify_charge().await { + let proxy = PowerProxy::new(&conn).await.unwrap(); + if let Ok(p) = proxy.receive_notify_charge_control_end_threshold().await { p.for_each(|e| { if let Ok(out) = e.args() { if notifs_enabled1.load(Ordering::SeqCst) { diff --git a/rog-control-center/src/page_states.rs b/rog-control-center/src/page_states.rs index 2148f52e..82d19706 100644 --- a/rog-control-center/src/page_states.rs +++ b/rog-control-center/src/page_states.rs @@ -45,7 +45,7 @@ impl BiosState { GpuMode::NotSupported }, panel_overdrive: if supported.rog_bios_ctrl.panel_overdrive { - dbus.proxies().rog_bios().panel_overdrive()? + dbus.proxies().rog_bios().panel_od()? } else { false }, @@ -258,7 +258,7 @@ pub struct PageDataStates { pub anime: AnimeState, pub profiles: ProfilesState, pub fan_curves: FanCurvesState, - pub charge_limit: i16, + pub charge_limit: u8, pub error: Option, } @@ -279,7 +279,7 @@ impl PageDataStates { keyboard_layout, notifs_enabled, was_notified: charge_notified, - charge_limit: dbus.proxies().charge().limit()?, + charge_limit: dbus.proxies().charge().charge_control_end_threshold()?, bios: BiosState::new(bios_notified, supported, dbus)?, aura: AuraState::new(aura_notified, supported, dbus)?, anime: AnimeState::new(anime_notified, supported, dbus)?, @@ -296,7 +296,7 @@ impl PageDataStates { ) -> Result { let mut notified = false; if self.was_notified.load(Ordering::SeqCst) { - self.charge_limit = dbus.proxies().charge().limit()?; + self.charge_limit = dbus.proxies().charge().charge_control_end_threshold()?; self.was_notified.store(false, Ordering::SeqCst); notified = true; } diff --git a/rog-control-center/src/startup_error.rs b/rog-control-center/src/startup_error.rs index 3540c3cb..c1a6a822 100644 --- a/rog-control-center/src/startup_error.rs +++ b/rog-control-center/src/startup_error.rs @@ -27,7 +27,7 @@ impl eframe::App for AppErrorShow { .add(Button::new(RichText::new("Okay").size(20.0))) .clicked() { - frame.quit(); + frame.close(); } }); }); diff --git a/rog-control-center/src/widgets/rog_bios.rs b/rog-control-center/src/widgets/rog_bios.rs index 82cc0398..7754c065 100644 --- a/rog-control-center/src/widgets/rog_bios.rs +++ b/rog-control-center/src/widgets/rog_bios.rs @@ -47,7 +47,7 @@ pub fn rog_bios_group( if ui.add(slider).drag_released() { dbus.proxies() .charge() - .set_limit(states.charge_limit as u8) + .set_charge_control_end_threshold(states.charge_limit as u8) .map_err(|err| { states.error = Some(err.to_string()); }) @@ -82,7 +82,7 @@ pub fn rog_bios_group( { dbus.proxies() .rog_bios() - .set_panel_overdrive(states.bios.panel_overdrive) + .set_panel_od(states.bios.panel_overdrive) .map_err(|err| { states.error = Some(err.to_string()); }) diff --git a/rog-control-center/src/widgets/top_bar.rs b/rog-control-center/src/widgets/top_bar.rs index f020d902..3e8af4ef 100644 --- a/rog-control-center/src/widgets/top_bar.rs +++ b/rog-control-center/src/widgets/top_bar.rs @@ -39,7 +39,7 @@ impl<'a> RogApp<'a> { Button::new(RichText::new("❌").size(height - 4.0)).frame(false), ); if close_response.clicked() { - frame.quit(); + frame.close(); } }); }); diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index b44eb28b..1bd15dd7 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -3,9 +3,9 @@ pub static DBUS_PATH: &str = "/org/asuslinux/Daemon"; pub static DBUS_IFACE: &str = "org.asuslinux.Daemon"; pub mod zbus_anime; -pub mod zbus_charge; pub mod zbus_led; pub mod zbus_platform; +pub mod zbus_power; pub mod zbus_profile; pub mod zbus_supported; @@ -19,7 +19,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct DbusProxiesBlocking<'a> { anime: zbus_anime::AnimeProxyBlocking<'a>, - charge: zbus_charge::ChargeProxyBlocking<'a>, + charge: zbus_power::PowerProxyBlocking<'a>, led: zbus_led::LedProxyBlocking<'a>, profile: zbus_profile::ProfileProxyBlocking<'a>, rog_bios: zbus_platform::RogBiosProxyBlocking<'a>, @@ -35,7 +35,7 @@ impl<'a> DbusProxiesBlocking<'a> { DbusProxiesBlocking { anime: zbus_anime::AnimeProxyBlocking::new(&conn)?, led: zbus_led::LedProxyBlocking::new(&conn)?, - charge: zbus_charge::ChargeProxyBlocking::new(&conn)?, + charge: zbus_power::PowerProxyBlocking::new(&conn)?, profile: zbus_profile::ProfileProxyBlocking::new(&conn)?, rog_bios: zbus_platform::RogBiosProxyBlocking::new(&conn)?, supported: zbus_supported::SupportedProxyBlocking::new(&conn)?, @@ -48,7 +48,7 @@ impl<'a> DbusProxiesBlocking<'a> { &self.anime } - pub fn charge(&self) -> &zbus_charge::ChargeProxyBlocking<'a> { + pub fn charge(&self) -> &zbus_power::PowerProxyBlocking<'a> { &self.charge } @@ -88,7 +88,7 @@ impl<'a> RogDbusClientBlocking<'a> { pub struct DbusProxies<'a> { anime: zbus_anime::AnimeProxy<'a>, - charge: zbus_charge::ChargeProxy<'a>, + charge: zbus_power::PowerProxy<'a>, led: zbus_led::LedProxy<'a>, profile: zbus_profile::ProfileProxy<'a>, rog_bios: zbus_platform::RogBiosProxy<'a>, @@ -104,7 +104,7 @@ impl<'a> DbusProxies<'a> { DbusProxies { anime: zbus_anime::AnimeProxy::new(&conn).await?, led: zbus_led::LedProxy::new(&conn).await?, - charge: zbus_charge::ChargeProxy::new(&conn).await?, + charge: zbus_power::PowerProxy::new(&conn).await?, profile: zbus_profile::ProfileProxy::new(&conn).await?, rog_bios: zbus_platform::RogBiosProxy::new(&conn).await?, supported: zbus_supported::SupportedProxy::new(&conn).await?, @@ -117,7 +117,7 @@ impl<'a> DbusProxies<'a> { &self.anime } - pub fn charge(&self) -> &zbus_charge::ChargeProxy<'a> { + pub fn charge(&self) -> &zbus_power::PowerProxy<'a> { &self.charge } diff --git a/rog-dbus/src/zbus_platform.rs b/rog-dbus/src/zbus_platform.rs index 9009dc65..b7824995 100644 --- a/rog-dbus/src/zbus_platform.rs +++ b/rog-dbus/src/zbus_platform.rs @@ -27,33 +27,53 @@ use zbus_macros::dbus_proxy; default_path = "/org/asuslinux/Platform" )] trait RogBios { - /// DedicatedGraphicMode method + /// DgpuDisable method + fn dgpu_disable(&self) -> zbus::Result; + + /// EgpuEnable method + fn egpu_enable(&self) -> zbus::Result; + + /// GpuMuxMode method fn gpu_mux_mode(&self) -> zbus::Result; + /// PanelOd method + fn panel_od(&self) -> zbus::Result; + /// PostBootSound method fn post_boot_sound(&self) -> zbus::Result; - /// SetDedicatedGraphicMode method + /// SetDgpuDisable method + fn set_dgpu_disable(&self, disable: bool) -> zbus::Result<()>; + + /// SetEgpuEnable method + fn set_egpu_enable(&self, enable: bool) -> zbus::Result<()>; + + /// SetGpuMuxMode method fn set_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>; + /// SetPanelOd method + fn set_panel_od(&self, overdrive: bool) -> zbus::Result<()>; + /// SetPostBootSound method fn set_post_boot_sound(&self, on: bool) -> zbus::Result<()>; - /// PanelOverdrive method - fn panel_overdrive(&self) -> zbus::Result; + /// NotifyDgpuDisable signal + #[dbus_proxy(signal)] + fn notify_dgpu_disable(&self, disable: bool) -> zbus::Result<()>; - /// SetPanelOverdrive method - fn set_panel_overdrive(&self, overdrive: bool) -> zbus::Result<()>; + /// NotifyEgpuEnable signal + #[dbus_proxy(signal)] + fn notify_egpu_enable(&self, enable: bool) -> zbus::Result<()>; - /// NotifyDedicatedGraphicMode signal + /// NotifyGpuMuxMode signal #[dbus_proxy(signal)] fn notify_gpu_mux_mode(&self, mode: GpuMode) -> zbus::Result<()>; + /// NotifyPanelOd signal + #[dbus_proxy(signal)] + fn notify_panel_od(&self, overdrive: bool) -> zbus::Result<()>; + /// NotifyPostBootSound signal #[dbus_proxy(signal)] - fn notify_post_boot_sound(&self, sound: bool) -> zbus::Result<()>; - - /// NotifyPanelOverdrive signal - #[dbus_proxy(signal)] - fn notify_panel_overdrive(&self, overdrive: bool) -> zbus::Result<()>; + fn notify_post_boot_sound(&self, on: bool) -> zbus::Result<()>; } diff --git a/rog-dbus/src/zbus_charge.rs b/rog-dbus/src/zbus_power.rs similarity index 64% rename from rog-dbus/src/zbus_charge.rs rename to rog-dbus/src/zbus_power.rs index eba1c1d2..37359c92 100644 --- a/rog-dbus/src/zbus_charge.rs +++ b/rog-dbus/src/zbus_power.rs @@ -23,16 +23,23 @@ use zbus_macros::dbus_proxy; #[dbus_proxy( interface = "org.asuslinux.Daemon", - default_path = "/org/asuslinux/Charge" + default_path = "/org/asuslinux/Power" )] -trait Charge { - /// Limit method - fn limit(&self) -> zbus::Result; +trait Power { + /// charge_control_end_threshold method + fn charge_control_end_threshold(&self) -> zbus::Result; - /// SetLimit method - fn set_limit(&self, limit: u8) -> zbus::Result<()>; + /// MainsOnline method + fn mains_online(&self) -> zbus::Result; + + /// set_charge_control_end_threshold method + fn set_charge_control_end_threshold(&self, limit: u8) -> zbus::Result<()>; /// NotifyCharge signal #[dbus_proxy(signal)] - fn notify_charge(&self, limit: u8) -> zbus::Result; + fn notify_charge_control_end_threshold(&self, limit: u8) -> zbus::Result; + + /// NotifyMainsOnline signal + #[dbus_proxy(signal)] + fn notify_mains_online(&self, on: bool) -> zbus::Result<()>; } diff --git a/rog-platform/Cargo.toml b/rog-platform/Cargo.toml index 2d9be5b5..4eb73f07 100644 --- a/rog-platform/Cargo.toml +++ b/rog-platform/Cargo.toml @@ -18,5 +18,6 @@ udev = "^0.6" rusb = "^0.9" sysfs-class = "^0.1" inotify = "^0.10.0" +# inotify = { version = "0.10.0", default-features = false } concat-idents = "1.1.3" \ No newline at end of file diff --git a/rog-platform/src/error.rs b/rog-platform/src/error.rs index 9be403da..08be205b 100644 --- a/rog-platform/src/error.rs +++ b/rog-platform/src/error.rs @@ -15,7 +15,8 @@ pub enum PlatformError { AttrNotFound(String), MissingFunction(String), MissingLedBrightNode(String, std::io::Error), - Io(String, std::io::Error), + IoPath(String, std::io::Error), + Io(std::io::Error), NoAuraKeyboard, NoAuraNode, } @@ -33,9 +34,10 @@ impl fmt::Display for PlatformError { PlatformError::Write(path, error) => write!(f, "Write {}: {}", path, error), PlatformError::NotSupported => write!(f, "Not supported"), PlatformError::AttrNotFound(deets) => write!(f, "Attribute not found: {}", deets), + PlatformError::Io(deets) => write!(f, "std::io error: {}", deets), PlatformError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets), PlatformError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error), - PlatformError::Io(path, detail) => write!(f, "std::io error: {} {}", path, detail), + PlatformError::IoPath(path, detail) => write!(f, "{} {}", path, detail), PlatformError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"), PlatformError::NoAuraNode => write!(f, "No Aura keyboard node found"), } @@ -49,3 +51,9 @@ impl From for PlatformError { PlatformError::USB(err) } } + +impl From for PlatformError { + fn from(err: std::io::Error) -> Self { + PlatformError::Io(err) + } +} diff --git a/rog-platform/src/hid_raw.rs b/rog-platform/src/hid_raw.rs index d332bb8a..a793b852 100644 --- a/rog-platform/src/hid_raw.rs +++ b/rog-platform/src/hid_raw.rs @@ -21,11 +21,13 @@ impl HidRaw { for device in enumerator .scan_devices() - .map_err(|e| PlatformError::Io("enumerator".to_owned(), e))? + .map_err(|e| PlatformError::IoPath("enumerator".to_owned(), e))? { if let Some(parent) = device .parent_with_subsystem_devtype("usb", "usb_device") - .map_err(|e| PlatformError::Io(device.devpath().to_string_lossy().to_string(), e))? + .map_err(|e| { + PlatformError::IoPath(device.devpath().to_string_lossy().to_string(), e) + })? { if let Some(parent) = parent.attribute_value("idProduct") { if parent == id_product { @@ -47,9 +49,9 @@ impl HidRaw { let mut file = OpenOptions::new() .write(true) .open(&self.0) - .map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e))?; + .map_err(|e| PlatformError::IoPath(self.0.to_string_lossy().to_string(), e))?; // println!("write: {:02x?}", &message); file.write_all(message) - .map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e)) + .map_err(|e| PlatformError::IoPath(self.0.to_string_lossy().to_string(), e)) } } diff --git a/rog-platform/src/lib.rs b/rog-platform/src/lib.rs index 88962bc8..8c118860 100644 --- a/rog-platform/src/lib.rs +++ b/rog-platform/src/lib.rs @@ -45,7 +45,7 @@ pub fn read_attr_bool(device: &Device, attr_name: &str) -> Result { pub fn write_attr_bool(device: &mut Device, attr: &str, value: bool) -> Result<()> { device .set_attribute_value(attr, &(value as u8).to_string()) - .map_err(|e| PlatformError::Io(attr.into(), e)) + .map_err(|e| PlatformError::IoPath(attr.into(), e)) } pub fn read_attr_u8(device: &Device, attr_name: &str) -> Result { @@ -59,7 +59,7 @@ pub fn read_attr_u8(device: &Device, attr_name: &str) -> Result { pub fn write_attr_u8(device: &mut Device, attr: &str, value: u8) -> Result<()> { device .set_attribute_value(attr, &(value).to_string()) - .map_err(|e| PlatformError::Io(attr.into(), e)) + .map_err(|e| PlatformError::IoPath(attr.into(), e)) } pub fn read_attr_u8_array(device: &Device, attr_name: &str) -> Result> { @@ -79,7 +79,7 @@ pub fn write_attr_u8_array(device: &mut Device, attr: &str, values: &[u8]) -> Re let tmp = tmp.trim(); device .set_attribute_value(attr, &tmp) - .map_err(|e| PlatformError::Io(attr.into(), e)) + .map_err(|e| PlatformError::IoPath(attr.into(), e)) } #[cfg(test)] diff --git a/rog-platform/src/macros.rs b/rog-platform/src/macros.rs index 244fdb94..0989b290 100644 --- a/rog-platform/src/macros.rs +++ b/rog-platform/src/macros.rs @@ -21,9 +21,19 @@ macro_rules! watch_attr { 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) + if let Some(path) = path.to_str() { + let mut inotify = inotify::Inotify::init()?; + inotify.add_watch(path, inotify::WatchMask::MODIFY) + .map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + PlatformError::AttrNotFound(format!("{}", $attr_name)) + } else { + PlatformError::IoPath(format!("{}", path), e) + } + })?; + return Ok(inotify); + } + Err(PlatformError::AttrNotFound(format!("{}", $attr_name))) } }); }; @@ -127,5 +137,6 @@ macro_rules! attr_u8_array { crate::has_attr!($attr_name $item); crate::get_attr_u8_array!($attr_name $item); crate::set_attr_u8_array!($attr_name $item); + crate::watch_attr!($attr_name $item); }; } diff --git a/rog-platform/src/platform.rs b/rog-platform/src/platform.rs index 46aee4bd..8658a28b 100644 --- a/rog-platform/src/platform.rs +++ b/rog-platform/src/platform.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use log::{info, warn}; use serde::{Deserialize, Serialize}; @@ -20,6 +20,7 @@ use crate::{ #[derive(Debug, PartialEq, PartialOrd, Clone)] pub struct AsusPlatform { path: PathBuf, + pp_path: PathBuf, } impl AsusPlatform { @@ -44,6 +45,7 @@ impl AsusPlatform { info!("Found platform support at {:?}", device.sysname()); return Ok(Self { path: device.syspath().to_owned(), + pp_path: PathBuf::from_str("/sys/firmware/acpi").unwrap(), }); } Err(PlatformError::MissingFunction( @@ -55,6 +57,10 @@ impl AsusPlatform { attr_bool!("egpu_enable", path); attr_bool!("panel_od", path); attr_u8!("gpu_mux_mode", path); + // This is technically the same as `platform_profile` since both are tied in-kernel + attr_u8!("throttle_thermal_policy", path); + // The acpi platform_profile support + attr_u8!("platform_profile", pp_path); } #[derive(Serialize, Deserialize, Type, Debug, PartialEq, Clone, Copy)] diff --git a/rog-platform/src/power.rs b/rog-platform/src/power.rs index d74633ff..ce25b525 100644 --- a/rog-platform/src/power.rs +++ b/rog-platform/src/power.rs @@ -102,4 +102,5 @@ impl AsusPower { } attr_u8!("charge_control_end_threshold", battery); + attr_u8!("online", mains); }