diff --git a/Cargo.lock b/Cargo.lock index c070fae0..f597e748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,7 @@ dependencies = [ "serde", "serde_derive", "sysfs-class", + "thiserror", "tokio", "toml", "uhid-virt", diff --git a/README.md b/README.md index 32611786..c6fcc27a 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,12 @@ If the daemon service is enabled then on boot the following will be reloaded fro The daemon also saves the settings per mode as the keyboard does not do this itself - this means cycling through modes with the Aura keys will use the settings that were used via CLI. +### DBUS Input + +Commands: `FanMode`, `LedWriteBytes`, `LedWriteMultizone`, `LedWriteEffect` + +TODO: fill in this info + ### Wireshark captures TODO: see `./wireshark_data/` for some captures. diff --git a/aura/src/aura_dbus.rs b/aura/src/aura_dbus.rs index 7fad8112..2799980f 100644 --- a/aura/src/aura_dbus.rs +++ b/aura/src/aura_dbus.rs @@ -89,7 +89,7 @@ impl AuraDbusWriter { ) -> Result<(), Box> { self.connection.process(Duration::from_micros(300))?; - let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteEffect")? + let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "LedWriteMultizone")? .append1(&group[0].to_vec()) .append1(&group[1].to_vec()) .append1(&group[2].to_vec()) @@ -115,6 +115,19 @@ impl AuraDbusWriter { Err(Box::new(dbus::Error::new_custom("name", "message"))) } + #[inline] + pub fn write_fan_mode(&self, level: u8) -> Result> { + let msg = + Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "FanMode")?.append1(level); + let r = self + .connection + .send_with_reply_and_block(msg, Duration::from_millis(5000))?; + if let Some(reply) = r.get1::<&str>() { + return Ok(reply.to_owned()); + } + Err(Box::new(dbus::Error::new_custom("name", "message"))) + } + #[inline] pub fn write_builtin_mode( &self, diff --git a/rog-core/Cargo.toml b/rog-core/Cargo.toml index 0e0d7965..2faa2a4c 100644 --- a/rog-core/Cargo.toml +++ b/rog-core/Cargo.toml @@ -43,3 +43,5 @@ intel-pstate = "^0.2.1" # virtualisation of HID, mainly for outputting consumer key codes uhid-virt = "^0.0.4" #keycode = "0.3" + +thiserror = "^1.0.15" \ No newline at end of file diff --git a/rog-core/src/daemon.rs b/rog-core/src/daemon.rs index 2990819f..84c63378 100644 --- a/rog-core/src/daemon.rs +++ b/rog-core/src/daemon.rs @@ -19,6 +19,7 @@ use std::sync::{mpsc, Arc}; use std::time::{Duration, Instant}; use tokio::sync::Mutex; +type FanModeType = Arc>>; type LedMsgType = Arc>>>; type EffectType = Arc>>>>; @@ -81,7 +82,7 @@ pub async fn start_daemon() -> Result<(), Box> { let (aura_command_send, aura_command_recv) = mpsc::sync_channel::(1); - let (tree, input, effect, effect_cancel_signal) = dbus_create_tree(); + let (tree, input, effect, fan_mode, effect_cancel_signal) = dbus_create_tree(); // We add the tree to the connection so that incoming method calls will be handled. tree.start_receive_send(&*connection); @@ -97,6 +98,15 @@ pub async fn start_daemon() -> Result<(), Box> { // start the keyboard reader and laptop-action loop let key_read_handle = tokio::spawn(async move { loop { + // Fan mode + if let Ok(mut lock) = fan_mode.try_lock() { + if let Some(n) = lock.take() { + let mut config = config1.lock().await; + rogcore + .fan_mode_set(n, &mut config) + .unwrap_or_else(|err| warn!("{:?}", err)); + } + } let acs = aura_command_send.clone(); let data = keyboard_reader.poll_keyboard().await; if let Some(bytes) = data { @@ -227,7 +237,10 @@ fn dbus_create_ledmultizone_method(effect: EffectType) -> Method { let byte_array: Vec> = vec![iter.read()?, iter.read()?, iter.read()?, iter.read()?]; *lock = Some(byte_array); - let mret = m.msg.method_return().append1(&format!("Got effect part")); + let mret = m + .msg + .method_return() + .append1(&"Got effect part".to_string()); Ok(vec![mret]) } else { Err(MethodErr::failed("Could not lock daemon for access")) @@ -263,14 +276,12 @@ fn dbus_create_ledeffect_method(effect: EffectType) -> Method { iter.read()?, ]; *lock = Some(byte_array); - let mret = m.msg.method_return().append1(&format!("Got effect part")); - Ok(vec![mret]) + Ok(vec![]) } else { Err(MethodErr::failed("Could not lock daemon for access")) } } }) - .outarg::<&str, _>("reply") .inarg::, _>("bytearray") .inarg::, _>("bytearray") .inarg::, _>("bytearray") @@ -284,9 +295,40 @@ fn dbus_create_ledeffect_method(effect: EffectType) -> Method { .inarg::, _>("bytearray") } -fn dbus_create_tree() -> (Tree, LedMsgType, EffectType, Arc>) { +fn dbus_create_fan_mode_method(fan_mode: FanModeType) -> Method { + let factory = Factory::new_sync::<()>(); + factory + // method for ledmessage + .method("FanMode", (), { + move |m| { + if let Ok(mut lock) = fan_mode.try_lock() { + let mut iter = m.msg.iter_init(); + let byte: u8 = iter.read()?; + *lock = Some(byte); + let mret = m + .msg + .method_return() + .append1(format!("Fan level set to {:?}", FanLevel::from(byte))); + Ok(vec![mret]) + } else { + Err(MethodErr::failed("Could not lock daemon for access")) + } + } + }) + .outarg::<&str, _>("reply") + .inarg::("byte") +} + +fn dbus_create_tree() -> ( + Tree, + LedMsgType, + EffectType, + FanModeType, + Arc>, +) { let input_bytes: LedMsgType = Arc::new(Mutex::new(None)); let input_effect: EffectType = Arc::new(Mutex::new(None)); + let fan_mode: FanModeType = Arc::new(Mutex::new(None)); let factory = Factory::new_sync::<()>(); let effect_cancel_sig = Arc::new(factory.signal("LedCancelEffect", ())); @@ -297,8 +339,9 @@ fn dbus_create_tree() -> (Tree, LedMsgType, EffectType, Arc Result<(), Box> { writer.write_multizone(&byte_arr)?; } _ => match writer.write_builtin_mode(&command) { - Ok(msg) => println!("Response: {}", msg), + Ok(msg) => println!("Daemon response: {}", msg), Err(err) => println!("Error: {}", err), }, } @@ -77,7 +77,13 @@ pub async fn main() -> Result<(), Box> { } if let Some(brightness) = parsed.bright { match writer.write_brightness(brightness.level()) { - Ok(msg) => println!("Response: {}", msg), + Ok(msg) => println!("Daemon response: {}", msg), + Err(err) => println!("Error: {}", err), + } + } + if let Some(fan_level) = parsed.fan_mode { + match writer.write_fan_mode(fan_level.into()) { + Ok(msg) => println!("Daemon response: {}", msg), Err(err) => println!("Error: {}", err), } } diff --git a/rog-core/src/rogcore.rs b/rog-core/src/rogcore.rs index c61d6549..076f6be5 100644 --- a/rog-core/src/rogcore.rs +++ b/rog-core/src/rogcore.rs @@ -1,6 +1,6 @@ // Return show-stopping errors, otherwise map error to a log level -use crate::{config::Config, virt_device::VirtKeys}; +use crate::{config::Config, error::RogError, virt_device::VirtKeys}; use log::{error, info, warn}; use rusb::DeviceHandle; use std::error::Error; @@ -10,6 +10,7 @@ use std::marker::{PhantomData, PhantomPinned}; use std::path::Path; use std::process::Command; use std::ptr::NonNull; +use std::str::FromStr; use std::time::Duration; static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy"; @@ -118,10 +119,21 @@ impl RogCore { Ok(()) } - pub fn fan_mode_step(&mut self, config: &mut Config) -> Result<(), Box> { + pub fn fan_mode_set(&mut self, n: u8, config: &mut Config) -> Result<(), Box> { let path = RogCore::get_fan_path()?; let mut fan_ctrl = OpenOptions::new().read(true).write(true).open(path)?; + info!("Fan mode set to: {:?}", FanLevel::from(n)); + config.fan_mode = n; + fan_ctrl + .write_all(format!("{:?}", config.fan_mode).as_bytes()) + .unwrap_or_else(|err| error!("Could not write to {}, {:?}", path, err)); + self.set_pstate_for_fan_mode(FanLevel::from(n), config)?; + config.write(); + Ok(()) + } + + pub fn fan_mode_step(&mut self, config: &mut Config) -> Result<(), Box> { let mut n = config.fan_mode; info!("Current fan mode: {:?}", FanLevel::from(n)); // wrap around the step number @@ -130,15 +142,7 @@ impl RogCore { } else { n = 0; } - info!("Fan mode stepped to: {:?}", FanLevel::from(n)); - fan_ctrl - .write_all(format!("{:?}", config.fan_mode).as_bytes()) - .unwrap_or_else(|err| error!("Could not write to {}, {:?}", path, err)); - self.set_pstate_for_fan_mode(FanLevel::from(n), config)?; - config.fan_mode = n; - config.write(); - - Ok(()) + self.fan_mode_set(n, config) } fn set_pstate_for_fan_mode( @@ -310,6 +314,19 @@ pub enum FanLevel { Silent, } +impl FromStr for FanLevel { + type Err = RogError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "normal" => Ok(FanLevel::Normal), + "boost" => Ok(FanLevel::Boost), + "silent" => Ok(FanLevel::Silent), + _ => Err(RogError::ParseFanLevel), + } + } +} + impl From for FanLevel { fn from(n: u8) -> Self { match n {