Fix reloading last keyboard brightness on boot

This commit is contained in:
Luke
2020-05-05 14:21:19 +12:00
parent 7a8af1266a
commit f11afdbc7d
13 changed files with 352 additions and 289 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "rog-daemon"
version = "0.9.3"
version = "0.9.4"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]

View File

@@ -1,8 +1,7 @@
// Return show-stopping errors, otherwise map error to a log level
use crate::{config::Config, virt_device::VirtKeys};
use log::{debug, error, info, warn};
use rog_aura::{aura_brightness_bytes, error::AuraError, BuiltInModeByte};
use log::{error, info, warn};
use rusb::DeviceHandle;
use std::error::Error;
use std::fs::OpenOptions;
@@ -13,16 +12,6 @@ use std::process::Command;
use std::ptr::NonNull;
use std::time::Duration;
static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
static LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
// Only these two packets must be 17 bytes
static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
@@ -75,22 +64,6 @@ impl RogCore {
})
}
pub async fn reload(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
let path = if Path::new(FAN_TYPE_1_PATH).exists() {
FAN_TYPE_1_PATH
} else if Path::new(FAN_TYPE_2_PATH).exists() {
FAN_TYPE_2_PATH
} else {
return Ok(());
};
let mut file = OpenOptions::new().write(true).open(path)?;
file.write_all(format!("{:?}\n", config.fan_mode).as_bytes())?;
self.set_pstate_for_fan_mode(FanLevel::from(config.fan_mode), config)?;
info!("Reloaded last saved settings");
Ok(())
}
pub fn virt_keys(&mut self) -> &mut VirtKeys {
&mut self.virt_keys
}
@@ -108,15 +81,30 @@ impl RogCore {
Err(rusb::Error::NoDevice)
}
pub fn fan_mode_step(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
let path = if Path::new(FAN_TYPE_1_PATH).exists() {
FAN_TYPE_1_PATH
fn get_fan_path() -> Result<&'static str, std::io::Error> {
if Path::new(FAN_TYPE_1_PATH).exists() {
Ok(FAN_TYPE_1_PATH)
} else if Path::new(FAN_TYPE_2_PATH).exists() {
FAN_TYPE_2_PATH
Ok(FAN_TYPE_2_PATH)
} else {
return Ok(());
};
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Fan mode not available",
))
}
}
pub async fn fan_mode_reload(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
let path = RogCore::get_fan_path()?;
let mut file = OpenOptions::new().write(true).open(path)?;
file.write_all(format!("{:?}\n", config.fan_mode).as_bytes())?;
self.set_pstate_for_fan_mode(FanLevel::from(config.fan_mode), config)?;
info!("Reloaded last saved settings");
Ok(())
}
pub fn fan_mode_step(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
let path = RogCore::get_fan_path()?;
let mut fan_ctrl = OpenOptions::new().read(true).write(true).open(path)?;
let mut buf = String::new();
@@ -300,220 +288,6 @@ where
}
}
pub enum AuraCommand {
BrightInc,
BrightDec,
BuiltinNext,
BuiltinPrev,
WriteBytes(Vec<u8>),
WriteEffect(Vec<Vec<u8>>),
ReloadLast,
}
/// UNSAFE: Must live as long as RogCore
///
/// Because we're holding a pointer to something that *may* go out of scope while the
/// pointer is held. We're relying on access to struct to be behind a Mutex, and for behaviour
/// that may cause invalididated pointer to cause the program to panic rather than continue.
pub struct LedWriter<'d, C: 'd>
where
C: rusb::UsbContext,
{
handle: NonNull<DeviceHandle<C>>,
bright_min_max: (u8, u8),
supported_modes: Vec<BuiltInModeByte>,
led_endpoint: u8,
initialised: bool,
_phantom: PhantomData<&'d DeviceHandle<C>>,
}
/// UNSAFE
unsafe impl<'d, C> Send for LedWriter<'d, C> where C: rusb::UsbContext {}
unsafe impl<'d, C> Sync for LedWriter<'d, C> where C: rusb::UsbContext {}
impl<'d, C> LedWriter<'d, C>
where
C: rusb::UsbContext,
{
pub fn new(
device_handle: NonNull<DeviceHandle<C>>,
led_endpoint: u8,
bright_min_max: (u8, u8),
supported_modes: Vec<BuiltInModeByte>,
) -> Self {
LedWriter {
handle: device_handle,
led_endpoint,
bright_min_max,
supported_modes,
initialised: false,
_phantom: PhantomData,
}
}
pub async fn do_command(
&mut self,
command: AuraCommand,
config: &mut Config,
) -> Result<(), AuraError> {
if !self.initialised {
self.write_bytes(&LED_INIT1).await?;
self.write_bytes(LED_INIT2.as_bytes()).await?;
self.write_bytes(&LED_INIT3).await?;
self.write_bytes(LED_INIT4.as_bytes()).await?;
self.write_bytes(&LED_INIT5).await?;
self.initialised = true;
}
match command {
AuraCommand::BrightInc => {
let mut bright = config.brightness;
if bright < self.bright_min_max.1 {
bright += 1;
config.brightness = bright;
let bytes = aura_brightness_bytes(bright);
self.set_and_save(&bytes, config).await?;
info!("Increased LED brightness to {:#?}", bright);
}
}
AuraCommand::BrightDec => {
let mut bright = config.brightness;
if bright > self.bright_min_max.0 {
bright -= 1;
config.brightness = bright;
let bytes = aura_brightness_bytes(bright);
self.set_and_save(&bytes, config).await?;
info!("Decreased LED brightness to {:#?}", bright);
}
}
AuraCommand::BuiltinNext => {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = config.current_mode[3];
let idx = self
.supported_modes
.binary_search(&mode_curr.into())
.unwrap();
let idx_next = if idx < self.supported_modes.len() - 1 {
idx + 1
} else {
0
};
self.set_builtin(config, idx_next).await?;
}
AuraCommand::BuiltinPrev => {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = config.current_mode[3];
let idx = self
.supported_modes
.binary_search(&mode_curr.into())
.unwrap();
let idx_next = if idx > 0 {
idx - 1
} else {
self.supported_modes.len() - 1
};
self.set_builtin(config, idx_next).await?;
}
AuraCommand::WriteBytes(bytes) => self.set_and_save(&bytes, config).await?,
AuraCommand::WriteEffect(effect) => self.write_effect(effect).await?,
AuraCommand::ReloadLast => self.reload_last_builtin(&config).await?,
}
Ok(())
}
/// Should only be used if the bytes you are writing are verified correct
async fn write_bytes(&mut self, message: &[u8]) -> Result<(), AuraError> {
match unsafe { self.handle.as_ref() }.write_interrupt(
self.led_endpoint,
message,
Duration::from_millis(5),
) {
Ok(_) => {}
Err(err) => match err {
rusb::Error::Timeout => {}
_ => error!("Failed to write to led interrupt: {:?}", err),
},
}
Ok(())
}
async fn write_array_of_bytes(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> {
for message in messages {
self.write_bytes(*message).await?;
self.write_bytes(&LED_SET).await?;
}
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY).await?;
Ok(())
}
/// Write an effect block
///
/// `aura_effect_init` must be called any effect routine, and called only once.
async fn write_effect(&self, effect: Vec<Vec<u8>>) -> Result<(), AuraError> {
for row in effect.iter() {
match unsafe { self.handle.as_ref() }.write_interrupt(
self.led_endpoint,
row,
Duration::from_millis(1),
) {
Ok(_) => {}
Err(err) => match err {
rusb::Error::Timeout => {}
_ => error!("Failed to write LED interrupt: {:?}", err),
},
}
}
Ok(())
}
/// Used to set a builtin mode and save the settings for it
async fn set_and_save(&mut self, bytes: &[u8], config: &mut Config) -> Result<(), AuraError> {
let mode = BuiltInModeByte::from(bytes[3]);
// safety pass-through of possible effect write
if bytes[1] == 0xbc {
self.write_bytes(bytes).await?;
return Ok(());
} else if self.supported_modes.contains(&mode) || bytes[1] == 0xba {
let messages = [bytes];
self.write_array_of_bytes(&messages).await?;
config.set_field_from(bytes);
config.write();
return Ok(());
}
warn!("{:?} not supported", mode);
Err(AuraError::NotSupported)
}
/// Used to set a builtin mode and save the settings for it
async fn reload_last_builtin(&mut self, config: &Config) -> Result<(), AuraError> {
let mode_curr = config.current_mode[3];
let mode = config
.builtin_modes
.get_field_from(mode_curr)
.unwrap()
.to_owned();
self.write_bytes(&mode).await?;
info!("Reloaded last built-in mode");
Ok(())
}
/// Select next Aura effect
///
/// If the current effect is the last one then the effect selected wraps around to the first.
async fn set_builtin(&mut self, config: &mut Config, index: usize) -> Result<(), AuraError> {
let mode_next = config
.builtin_modes
.get_field_from(self.supported_modes[index].into())
.unwrap()
.to_owned();
println!("{:X?}", &mode_next);
self.set_and_save(&mode_next, config).await?;
info!("Switched LED mode to {:#?}", self.supported_modes[index]);
Ok(())
}
}
#[derive(Debug)]
enum FanLevel {
Normal,

View File

@@ -1,9 +1,16 @@
use crate::{config::Config, core::*, laptops::match_laptop};
use crate::{
config::Config,
core::*,
laptops::match_laptop,
led_control::{AuraCommand, LedWriter},
};
use dbus::{
channel::Sender,
nonblock::Process,
tree::{Factory, MTSync, Method, MethodErr, Signal, Tree},
};
use dbus_tokio::connection;
use log::{error, info, warn};
use rog_aura::{DBUS_IFACE, DBUS_PATH};
@@ -46,7 +53,7 @@ pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
);
// Reload settings
rogcore.reload(&mut config).await?;
rogcore.fan_mode_reload(&mut config).await?;
let mut led_writer = LedWriter::new(
rogcore.get_raw_device_handle(),
laptop.led_endpoint(),

View File

@@ -1,5 +1,4 @@
use crate::config::Config;
use crate::core::{AuraCommand, RogCore};
use crate::{config::Config, core::RogCore, led_control::AuraCommand};
use rog_aura::{error::AuraError, BuiltInModeByte};
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
use crate::virt_device::ConsumerKeys;

235
rog-core/src/led_control.rs Normal file
View File

@@ -0,0 +1,235 @@
static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
static LED_INIT4: &str = "^ASUS Tech.Inc."; // ^ == 0x5e
static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
// Only these two packets must be 17 bytes
static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
use crate::config::Config;
use log::{error, info, warn};
use rog_aura::{aura_brightness_bytes, error::AuraError, BuiltInModeByte};
use rusb::DeviceHandle;
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::time::Duration;
pub enum AuraCommand {
BrightInc,
BrightDec,
BuiltinNext,
BuiltinPrev,
WriteBytes(Vec<u8>),
WriteEffect(Vec<Vec<u8>>),
ReloadLast,
}
/// UNSAFE: Must live as long as RogCore
///
/// Because we're holding a pointer to something that *may* go out of scope while the
/// pointer is held. We're relying on access to struct to be behind a Mutex, and for behaviour
/// that may cause invalididated pointer to cause the program to panic rather than continue.
pub struct LedWriter<'d, C: 'd>
where
C: rusb::UsbContext,
{
handle: NonNull<DeviceHandle<C>>,
bright_min_max: (u8, u8),
supported_modes: Vec<BuiltInModeByte>,
led_endpoint: u8,
initialised: bool,
_phantom: PhantomData<&'d DeviceHandle<C>>,
}
/// UNSAFE
unsafe impl<'d, C> Send for LedWriter<'d, C> where C: rusb::UsbContext {}
unsafe impl<'d, C> Sync for LedWriter<'d, C> where C: rusb::UsbContext {}
impl<'d, C> LedWriter<'d, C>
where
C: rusb::UsbContext,
{
pub fn new(
device_handle: NonNull<DeviceHandle<C>>,
led_endpoint: u8,
bright_min_max: (u8, u8),
supported_modes: Vec<BuiltInModeByte>,
) -> Self {
LedWriter {
handle: device_handle,
led_endpoint,
bright_min_max,
supported_modes,
initialised: false,
_phantom: PhantomData,
}
}
pub async fn do_command(
&mut self,
command: AuraCommand,
config: &mut Config,
) -> Result<(), AuraError> {
if !self.initialised {
self.write_bytes(&LED_INIT1).await?;
self.write_bytes(LED_INIT2.as_bytes()).await?;
self.write_bytes(&LED_INIT3).await?;
self.write_bytes(LED_INIT4.as_bytes()).await?;
self.write_bytes(&LED_INIT5).await?;
self.initialised = true;
}
match command {
AuraCommand::BrightInc => {
let mut bright = config.brightness;
if bright < self.bright_min_max.1 {
bright += 1;
config.brightness = bright;
let bytes = aura_brightness_bytes(bright);
self.set_and_save(&bytes, config).await?;
info!("Increased LED brightness to {:#?}", bright);
}
}
AuraCommand::BrightDec => {
let mut bright = config.brightness;
if bright > self.bright_min_max.0 {
bright -= 1;
config.brightness = bright;
let bytes = aura_brightness_bytes(bright);
self.set_and_save(&bytes, config).await?;
info!("Decreased LED brightness to {:#?}", bright);
}
}
AuraCommand::BuiltinNext => {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = config.current_mode[3];
let idx = self
.supported_modes
.binary_search(&mode_curr.into())
.unwrap();
let idx_next = if idx < self.supported_modes.len() - 1 {
idx + 1
} else {
0
};
self.set_builtin(config, idx_next).await?;
}
AuraCommand::BuiltinPrev => {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = config.current_mode[3];
let idx = self
.supported_modes
.binary_search(&mode_curr.into())
.unwrap();
let idx_next = if idx > 0 {
idx - 1
} else {
self.supported_modes.len() - 1
};
self.set_builtin(config, idx_next).await?;
}
AuraCommand::WriteBytes(bytes) => self.set_and_save(&bytes, config).await?,
AuraCommand::WriteEffect(effect) => self.write_effect(effect).await?,
AuraCommand::ReloadLast => self.reload_last_builtin(&config).await?,
}
Ok(())
}
/// Should only be used if the bytes you are writing are verified correct
async fn write_bytes(&mut self, message: &[u8]) -> Result<(), AuraError> {
match unsafe { self.handle.as_ref() }.write_interrupt(
self.led_endpoint,
message,
Duration::from_millis(5),
) {
Ok(_) => {}
Err(err) => match err {
rusb::Error::Timeout => {}
_ => error!("Failed to write to led interrupt: {:?}", err),
},
}
Ok(())
}
async fn write_array_of_bytes(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> {
for message in messages {
self.write_bytes(*message).await?;
self.write_bytes(&LED_SET).await?;
}
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY).await?;
Ok(())
}
/// Write an effect block
///
/// `aura_effect_init` must be called any effect routine, and called only once.
async fn write_effect(&self, effect: Vec<Vec<u8>>) -> Result<(), AuraError> {
for row in effect.iter() {
match unsafe { self.handle.as_ref() }.write_interrupt(
self.led_endpoint,
row,
Duration::from_millis(1),
) {
Ok(_) => {}
Err(err) => match err {
rusb::Error::Timeout => {}
_ => error!("Failed to write LED interrupt: {:?}", err),
},
}
}
Ok(())
}
/// Used to set a builtin mode and save the settings for it
async fn set_and_save(&mut self, bytes: &[u8], config: &mut Config) -> Result<(), AuraError> {
let mode = BuiltInModeByte::from(bytes[3]);
// safety pass-through of possible effect write
if bytes[1] == 0xbc {
self.write_bytes(bytes).await?;
return Ok(());
} else if self.supported_modes.contains(&mode) || bytes[1] == 0xba {
let messages = [bytes];
self.write_array_of_bytes(&messages).await?;
config.set_field_from(bytes);
config.write();
return Ok(());
}
warn!("{:?} not supported", mode);
Err(AuraError::NotSupported)
}
/// Used to set a builtin mode and save the settings for it
async fn reload_last_builtin(&mut self, config: &Config) -> Result<(), AuraError> {
let mode_curr = config.current_mode[3];
let mode = config
.builtin_modes
.get_field_from(mode_curr)
.unwrap()
.to_owned();
self.write_bytes(&mode).await?;
// Reload brightness too
let bright = config.brightness;
let bytes = aura_brightness_bytes(bright);
self.write_bytes(&bytes).await?;
info!("Reloaded last used mode and brightness");
Ok(())
}
/// Select next Aura effect
///
/// If the current effect is the last one then the effect selected wraps around to the first.
async fn set_builtin(&mut self, config: &mut Config, index: usize) -> Result<(), AuraError> {
let mode_next = config
.builtin_modes
.get_field_from(self.supported_modes[index].into())
.unwrap()
.to_owned();
println!("{:X?}", &mode_next);
self.set_and_save(&mode_next, config).await?;
info!("Switched LED mode to {:#?}", self.supported_modes[index]);
Ok(())
}
}

View File

@@ -7,5 +7,7 @@ mod core;
pub mod daemon;
/// Laptop matching to determine capabilities
mod laptops;
///
mod led_control;
/// A virtual "consumer device" to help emit the correct key codes
mod virt_device;

View File

@@ -7,7 +7,7 @@ use rog_aura::{
AuraDbusWriter, LED_MSG_LEN,
};
static VERSION: &'static str = "0.9.3";
static VERSION: &'static str = "0.9.4";
#[derive(Debug, Options)]
struct CLIStart {