Merge branch 'fluke/inotify' into 'main'

Fluke/inotify

See merge request asus-linux/asusctl!133
This commit is contained in:
Luke Jones
2022-09-24 02:39:44 +00:00
41 changed files with 1428 additions and 1187 deletions

View File

@@ -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

33
Cargo.lock generated
View File

@@ -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"

View File

@@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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() {

View File

@@ -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);
}
}

View File

@@ -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 <luke@ljones.dev>"]
@@ -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"

View File

@@ -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<Mutex<CtrlAnime>>,
}
impl CtrlAnimeTask {
pub async fn new(inner: Arc<Mutex<CtrlAnime>>) -> 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<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
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<Mutex<CtrlAnime>>);
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(())
}
}

View File

@@ -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<Mutex<CtrlAnime>>);
/// 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<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
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(())
}
}

View File

@@ -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<Mutex<CtrlAnime>>);
/// 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<()>;
}

View File

@@ -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;
}

View File

@@ -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<Mutex<CtrlKbdLed>>,
}
impl CtrlKbdLedTask {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> 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<CtrlKbdLed>| {
// 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<Mutex<CtrlKbdLed>>);
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<Mutex<CtrlKbdLed>>);
impl CtrlKbdLedZbus {
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
Self(inner)
}
}
impl CtrlKbdLed {
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
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;

View File

@@ -1,3 +1,4 @@
pub mod config;
pub mod controller;
pub mod zbus;
/// Implements CtrlTask, Reloadable, ZbusRun
pub mod trait_impls;

View File

@@ -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<Mutex<CtrlKbdLed>>);
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<AuraDev1866>,
/// pub x19b6: Vec<AuraDev19b6>,
/// }
/// 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<AuraModeNum, AuraEffect> {
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<CtrlKbdLed>| {
// 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(())
}
}

View File

@@ -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<AuraDev1866>,
/// pub x19b6: Vec<AuraDev19b6>,
/// }
/// 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<AuraModeNum, AuraEffect> {
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<()>;
}

View File

@@ -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<Mutex<Config>>,
}
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<Mutex<Config>>) -> Result<Self, RogError> {
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(())
}
}

View File

@@ -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(())
}
}

View File

@@ -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<Self, RogError> {
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<Mutex<CtrlPlatformProfile>>,
}
impl CtrlProfileTask {
pub fn new(ctrl: Arc<Mutex<CtrlPlatformProfile>>) -> 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(())
}
}

View File

@@ -1,3 +1,4 @@
pub mod config;
pub mod controller;
pub mod zbus;
/// Implements CtrlTask, Reloadable, ZbusRun
pub mod trait_impls;

View File

@@ -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<Mutex<CtrlPlatformProfile>>);
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
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<Profile> {
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<Vec<Profile>> {
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<FanCurveSet> {
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(())
}
}

View File

@@ -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<Mutex<CtrlPlatformProfile>>,
}
impl ProfileZbus {
pub fn new(inner: Arc<Mutex<CtrlPlatformProfile>>) -> Self {
Self { inner }
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl ProfileZbus {
/// Fetch profile names
fn profiles(&mut self) -> zbus::fdo::Result<Vec<Profile>> {
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<Profile> {
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<Vec<Profile>> {
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<FanCurveSet> {
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;
}
}

View File

@@ -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(),
}
}
}

View File

@@ -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<dyn std::error::Error>> {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
@@ -61,14 +58,12 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
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<dyn Error>> {
async fn start_daemon() -> Result<(), Box<dyn Error>> {
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<dyn Error>>
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<dyn Error>>
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<dyn Error>>
// 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<T>(mut zbus: T, connection: &mut Connection) -> Result<(), Box<dyn Error>>
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(())
}

View File

@@ -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_<name>()`. Self requires the following methods to be available:
/// - `<name>() -> SomeValue`, functionally is a getter, but is allowed to have side effects.
/// - `notify_<name>(SignalContext, SomeValue)`
///
/// In most cases if `SomeValue` is stored in a config then `<name>()` 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<SignalContext<'static>, 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;

View File

@@ -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

View File

@@ -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 {
<things the controller requires>
config: Arc<Mutex<Config>>,
}
// 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 {
<things the controller requires>
}
// Like this
#[derive(Clone)]
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);
impl CtrlAnime {
<functions the controller exposes>
}
#[derive(Clone)]
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
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<Mutex<CtrlAnime>>);
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() {
<some action>
}
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let lock self.inner.lock().await;
<some action>
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<Mutex<CtrlAnime>>);
impl crate::Reloadable for CtrlAnimeReloader {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(lock) = self.inner.try_lock() {
<some action>
}
async fn reload(&mut self) -> Result<(), RogError> {
let lock = self.inner.lock().await;
<some action>
Ok(())
}
}
```
The Zbus requirements:
```rust
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);
@@ -106,10 +137,9 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus {
fn <zbus method>() {
if let Ok(lock) = self.inner.try_lock() {
<some action>
}
async fn <zbus method>() {
let lock = self.inner.lock().await;
<some action>
}
}
```

View File

@@ -156,7 +156,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
start_closed = false;
continue;
}
dbg!("asda");
}
Ok(())
}

View File

@@ -65,7 +65,7 @@ impl Bios {
pub fn gpu_mux_mode(&self) -> Result<i16> {
Ok(1)
}
pub fn panel_overdrive(&self) -> Result<i16> {
pub fn panel_od(&self) -> Result<i16> {
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,
},

View File

@@ -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) {

View File

@@ -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<String>,
}
@@ -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<bool> {
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;
}

View File

@@ -27,7 +27,7 @@ impl eframe::App for AppErrorShow {
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
frame.quit();
frame.close();
}
});
});

View File

@@ -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());
})

View File

@@ -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();
}
});
});

View File

@@ -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
}

View File

@@ -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<bool>;
/// EgpuEnable method
fn egpu_enable(&self) -> zbus::Result<bool>;
/// GpuMuxMode method
fn gpu_mux_mode(&self) -> zbus::Result<GpuMode>;
/// PanelOd method
fn panel_od(&self) -> zbus::Result<bool>;
/// PostBootSound method
fn post_boot_sound(&self) -> zbus::Result<i16>;
/// 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<bool>;
/// 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<()>;
}

View File

@@ -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<i16>;
trait Power {
/// charge_control_end_threshold method
fn charge_control_end_threshold(&self) -> zbus::Result<u8>;
/// SetLimit method
fn set_limit(&self, limit: u8) -> zbus::Result<()>;
/// MainsOnline method
fn mains_online(&self) -> zbus::Result<bool>;
/// 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<u8>;
fn notify_charge_control_end_threshold(&self, limit: u8) -> zbus::Result<u8>;
/// NotifyMainsOnline signal
#[dbus_proxy(signal)]
fn notify_mains_online(&self, on: bool) -> zbus::Result<()>;
}

View File

@@ -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"

View File

@@ -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<rusb::Error> for PlatformError {
PlatformError::USB(err)
}
}
impl From<std::io::Error> for PlatformError {
fn from(err: std::io::Error) -> Self {
PlatformError::Io(err)
}
}

View File

@@ -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))
}
}

View File

@@ -45,7 +45,7 @@ pub fn read_attr_bool(device: &Device, attr_name: &str) -> Result<bool> {
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<u8> {
@@ -59,7 +59,7 @@ pub fn read_attr_u8(device: &Device, attr_name: &str) -> Result<u8> {
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<Vec<u8>> {
@@ -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)]

View File

@@ -21,9 +21,19 @@ macro_rules! watch_attr {
pub fn fn_name(&self) -> Result<inotify::Inotify> {
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);
};
}

View File

@@ -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)]

View File

@@ -102,4 +102,5 @@ impl AsusPower {
}
attr_u8!("charge_control_end_threshold", battery);
attr_u8!("online", mains);
}