Logging, better error handling

This commit is contained in:
Luke
2020-04-18 19:51:23 +12:00
parent ed74ca3d58
commit 5b24d24659
12 changed files with 310 additions and 123 deletions

View File

@@ -11,4 +11,5 @@ serde = "1.0"
serde_derive = "1.0"
toml = "0.5"
sysfs-class = "0.1.2"
aho-corasick = "0.7"
aho-corasick = "0.7"
thiserror = "1.0.15"

View File

@@ -55,7 +55,7 @@ impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
SetAuraBuiltin::Cycle(_) => msg[3] = 0x02,
SetAuraBuiltin::Rainbow(_) => msg[3] = 0x03,
SetAuraBuiltin::Rain(_) => msg[3] = 0x04,
SetAuraBuiltin::Random(_) => msg[3] = 0x05,
SetAuraBuiltin::Disco(_) => msg[3] = 0x05,
SetAuraBuiltin::Highlight(_) => msg[3] = 0x06,
SetAuraBuiltin::Laser(_) => msg[3] = 0x07,
SetAuraBuiltin::Ripple(_) => msg[3] = 0x08,
@@ -79,7 +79,7 @@ impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
msg[11] = settings.colour2.1;
msg[12] = settings.colour2.2;
}
SetAuraBuiltin::Cycle(settings) | SetAuraBuiltin::Random(settings) => {
SetAuraBuiltin::Cycle(settings) | SetAuraBuiltin::Disco(settings) => {
msg[7] = settings.speed as u8;
}
SetAuraBuiltin::Rain(settings)
@@ -172,7 +172,7 @@ impl Default for BuiltInModeBytes {
SingleSpeedDirection::default(),
)),
rain: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Rain(SingleColourSpeed::default())),
random: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Random(SingleSpeed::default())),
random: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Disco(SingleSpeed::default())),
highlight: <[u8; LED_MSG_LEN]>::from(SetAuraBuiltin::Highlight(
SingleColourSpeed::default(),
)),

View File

@@ -147,7 +147,7 @@ pub enum SetAuraBuiltin {
#[options(help = "random pattern mimicking raindrops")]
Rain(SingleColourSpeed),
#[options(help = "random pattern of three preset colours")]
Random(SingleSpeed),
Disco(SingleSpeed),
#[options(help = "pressed keys are highlighted to fade")]
Highlight(SingleColourSpeed),
#[options(help = "pressed keys generate horizontal laser")]

View File

@@ -1,13 +1,12 @@
use crate::{aura::BuiltInModeByte, config::Config, error::AuraError, laptops::*};
use aho_corasick::AhoCorasick;
use gumdrop::Options;
use rusb::{DeviceHandle, Error};
use rusb::DeviceHandle;
use std::cell::{Ref, RefCell};
use std::process::Command;
use std::str::FromStr;
use std::time::Duration;
use sysfs_class::Brightness;
use sysfs_class::SysClass;
use sysfs_class::{Brightness, SysClass};
pub const LED_MSG_LEN: usize = 17;
static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
@@ -20,33 +19,6 @@ static LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
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];
#[derive(Debug, Options)]
pub struct LedBrightness {
level: u8,
}
impl LedBrightness {
pub fn level(&self) -> u8 {
self.level
}
}
impl FromStr for LedBrightness {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"off" => Ok(LedBrightness { level: 0x00 }),
"low" => Ok(LedBrightness { level: 0x01 }),
"med" => Ok(LedBrightness { level: 0x02 }),
"high" => Ok(LedBrightness { level: 0x03 }),
_ => {
println!("Missing required argument, must be one of:\noff,low,med,high\n");
Err(AuraError::ParseBrightness)
}
}
}
}
/// ROG device controller
///
/// For the GX502GW the LED setup sequence looks like:
@@ -65,9 +37,8 @@ pub struct RogCore {
}
impl RogCore {
pub fn new() -> Result<RogCore, Error> {
// TODO: use /sys/class/dmi/id/board_name to detect model
let laptop = LaptopGX502GW::new();
pub fn new() -> Result<RogCore, AuraError> {
let laptop = match_laptop()?;
let mut dev_handle = RogCore::get_device(laptop.usb_vendor(), laptop.usb_product())?;
dev_handle.set_active_configuration(0).unwrap_or(());
@@ -78,7 +49,7 @@ impl RogCore {
for iface in dev_config.interfaces() {
for desc in iface.descriptors() {
for endpoint in desc.endpoint_descriptors() {
if endpoint.address() == 0x81 {
if endpoint.address() == laptop.led_iface_num() {
led_interface_num = desc.interface_number();
break;
}
@@ -93,7 +64,7 @@ impl RogCore {
initialised: false,
led_interface_num,
config: Config::default().read(),
laptop: RefCell::new(Box::new(laptop)),
laptop: RefCell::new(laptop),
})
}
@@ -109,17 +80,20 @@ impl RogCore {
&mut self.config
}
fn get_device(vendor: u16, product: u16) -> Result<DeviceHandle<rusb::GlobalContext>, Error> {
fn get_device(
vendor: u16,
product: u16,
) -> Result<DeviceHandle<rusb::GlobalContext>, AuraError> {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
return device.open();
return device.open().map_err(|err| AuraError::from(err));
}
}
Err(Error::NoDevice)
Err(AuraError::from(rusb::Error::NoDevice))
}
fn aura_write_messages(&mut self, messages: &[&[u8]]) -> Result<(), Error> {
fn aura_write_messages(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> {
self.handle.claim_interface(self.led_interface_num)?;
// Declared as a zoomy so that it is hidden
let write = |message: &[u8]| {
@@ -146,7 +120,7 @@ impl RogCore {
Ok(())
}
pub fn aura_brightness_bytes(brightness: u8) -> Result<[u8; 17], Error> {
pub fn aura_brightness_bytes(brightness: u8) -> Result<[u8; 17], AuraError> {
// TODO: check brightness range
let mut bright = [0u8; LED_MSG_LEN];
bright[0] = 0x5a;
@@ -157,7 +131,7 @@ impl RogCore {
Ok(bright)
}
pub fn aura_set_and_save(&mut self, bytes: &[u8]) -> Result<(), Error> {
pub fn aura_set_and_save(&mut self, bytes: &[u8]) -> Result<(), AuraError> {
let mode = BuiltInModeByte::from(bytes[3]);
if self.laptop().supported_modes().contains(&mode) || bytes[1] == 0xba {
let messages = [bytes];
@@ -166,23 +140,22 @@ impl RogCore {
self.config.write();
return Ok(());
}
Err(Error::NotSupported)
Err(AuraError::NotSupported)
}
pub fn poll_keyboard(&self, buf: &mut [u8; 32]) -> Result<Option<usize>, Error> {
pub fn poll_keyboard(&self, buf: &mut [u8; 32]) -> Result<Option<usize>, AuraError> {
match self
.handle
.read_interrupt(0x83, buf, Duration::from_micros(10))
{
Ok(o) => {
if buf[0] == 0x5a {
if buf[0] == self.laptop.borrow().hotkey_group_byte() {
return Ok(Some(o));
}
}
Err(err) => match err {
//Error::Timeout => {}
_ => return Err(err),
},
Err(err) => {
return Err(AuraError::from(err));
}
}
Ok(None)
}
@@ -194,17 +167,22 @@ impl RogCore {
.expect("failed to suspend");
}
pub fn toggle_airplane_mode(&self) {
if let Ok(output) = Command::new("rfkill").arg("list").output() {
if output.status.success() {
let patterns = &["yes"];
let ac = AhoCorasick::new(patterns);
if ac.earliest_find(output.stdout).is_some() {
Command::new("rfkill").arg("unblock").arg("all").spawn();
} else {
Command::new("rfkill").arg("block").arg("all").spawn();
pub fn toggle_airplane_mode(&self) -> Result<(), AuraError> {
match Command::new("rfkill").arg("list").output() {
Ok(output) => {
if output.status.success() {
let patterns = &["yes"];
let ac = AhoCorasick::new(patterns);
if ac.earliest_find(output.stdout).is_some() {
Command::new("rfkill").arg("unblock").arg("all").spawn()?;
} else {
Command::new("rfkill").arg("block").arg("all").spawn()?;
}
return Ok(());
}
return Err(AuraError::CommandFailed);
}
Err(err) => Err(AuraError::from(err)),
}
}
}
@@ -248,3 +226,30 @@ impl Backlight {
}
}
}
#[derive(Debug, Options)]
pub struct LedBrightness {
level: u8,
}
impl LedBrightness {
pub fn level(&self) -> u8 {
self.level
}
}
impl FromStr for LedBrightness {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"off" => Ok(LedBrightness { level: 0x00 }),
"low" => Ok(LedBrightness { level: 0x01 }),
"med" => Ok(LedBrightness { level: 0x02 }),
"high" => Ok(LedBrightness { level: 0x03 }),
_ => {
println!("Missing required argument, must be one of:\noff,low,med,high\n");
Err(AuraError::ParseBrightness)
}
}
}
}

View File

@@ -1,38 +1,24 @@
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display};
use std::fmt::Debug;
use thiserror::Error;
#[derive(PartialEq)]
#[derive(Error, Debug)]
pub enum AuraError {
#[error("unable to parse string to colour")]
ParseColour,
#[error("unable to parse string to speed")]
ParseSpeed,
#[error("unable to parse string to direction")]
ParseDirection,
#[error("unable to parse string to brightness")]
ParseBrightness,
#[error("could not poll the keyboard for input")]
PollKeyboard,
}
impl Debug for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Display for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Error for AuraError {
fn description(&self) -> &str {
match self {
AuraError::ParseColour => "could not parse colour",
AuraError::ParseSpeed => "could not parse speed",
AuraError::ParseDirection => "could not parse direction",
AuraError::ParseBrightness => "could not parse brightness",
AuraError::PollKeyboard => "failed to poll keyboard",
}
}
#[error("mode not supported")]
NotSupported,
#[error("USB error")]
UsbError(#[from] rusb::Error),
#[error("IO error")]
IOError(#[from] std::io::Error),
#[error("external command failed")]
CommandFailed,
}

View File

@@ -1,14 +1,30 @@
use crate::aura::BuiltInModeByte;
use crate::core::{Backlight, RogCore};
use crate::error::AuraError;
// ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="gdbus call
// --session --dest org.gnome.SettingsDaemon.Power
// --object-path /org/gnome/SettingsDaemon/Power
// --method org.freedesktop.DBus.Properties.Set org.gnome.SettingsDaemon.Power.Screen Brightness '<int32 65>'"
pub fn match_laptop() -> Result<Box<dyn Laptop>, AuraError> {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name()?;
match board_name.as_str() {
// The hell does it have a \n for anyway?
"GX502GW\n" => Ok(Box::new(LaptopGX502GW::new())),
_ => {
panic!("could not match laptop");
}
}
}
/// All laptop models should implement this trait
///
/// `do_hotkey_action` is passed the byte that a hotkey emits, and is expected to
/// perform whichever action matches that. For now the only key bytes passed in are
/// the ones which match `byte[0] == hotkey_group_byte`. On the GX502GW the keyboard
/// has 3 explicit groups: main, vol+media, and the ones that the Linux kernel doesn't
/// map.
pub trait Laptop {
fn do_hotkey_action(&self, core: &mut RogCore, key_byte: u8);
fn do_hotkey_action(&self, core: &mut RogCore, key_byte: u8) -> Result<(), AuraError>;
fn hotkey_group_byte(&self) -> u8;
fn led_iface_num(&self) -> u8;
fn supported_modes(&self) -> &[BuiltInModeByte];
fn usb_vendor(&self) -> u16;
fn usb_product(&self) -> u16;
@@ -22,9 +38,10 @@ pub struct LaptopGX502GW {
board_name: &'static str,
prod_family: &'static str,
hotkey_group_byte: u8,
min_bright: u8,
max_bright: u8,
supported_modes: Vec<BuiltInModeByte>,
min_led_bright: u8,
max_led_bright: u8,
led_iface_num: u8,
supported_modes: [BuiltInModeByte; 12],
backlight: Backlight,
}
@@ -37,9 +54,10 @@ impl LaptopGX502GW {
board_name: "GX502GW",
prod_family: "Zephyrus S",
hotkey_group_byte: 0x5a,
min_bright: 0x00,
max_bright: 0x03,
supported_modes: vec![
min_led_bright: 0x00,
max_led_bright: 0x03,
led_iface_num: 0x81,
supported_modes: [
BuiltInModeByte::Stable,
BuiltInModeByte::Breathe,
BuiltInModeByte::Cycle,
@@ -58,28 +76,28 @@ impl LaptopGX502GW {
}
}
impl Laptop for LaptopGX502GW {
fn do_hotkey_action(&self, rogcore: &mut RogCore, key_byte: u8) {
fn do_hotkey_action(&self, rogcore: &mut RogCore, key_byte: u8) -> Result<(), AuraError> {
match GX502GWKeys::from(key_byte) {
GX502GWKeys::Rog => {
println!("ROG!");
}
GX502GWKeys::LedBrightUp => {
let mut bright = rogcore.config().brightness;
if bright < self.max_bright {
if bright < self.max_led_bright {
bright += 1;
rogcore.config_mut().brightness = bright;
}
let bytes = RogCore::aura_brightness_bytes(bright).unwrap();
rogcore.aura_set_and_save(&bytes).unwrap();
let bytes = RogCore::aura_brightness_bytes(bright)?;
rogcore.aura_set_and_save(&bytes)?;
}
GX502GWKeys::LedBrightDown => {
let mut bright = rogcore.config().brightness;
if bright > self.min_bright {
if bright > self.min_led_bright {
bright -= 1;
rogcore.config_mut().brightness = bright;
}
let bytes = RogCore::aura_brightness_bytes(bright).unwrap();
rogcore.aura_set_and_save(&bytes).unwrap();
let bytes = RogCore::aura_brightness_bytes(bright)?;
rogcore.aura_set_and_save(&bytes)?;
}
GX502GWKeys::AuraNext => {
let mut mode = rogcore.config().current_mode[3] + 1;
@@ -90,7 +108,7 @@ impl Laptop for LaptopGX502GW {
}
rogcore.config_mut().current_mode[3] = mode;
if let Some(bytes) = rogcore.config_mut().get_current() {
rogcore.aura_set_and_save(&bytes).unwrap();
rogcore.aura_set_and_save(&bytes)?;
}
}
GX502GWKeys::AuraPrevious => {
@@ -104,7 +122,7 @@ impl Laptop for LaptopGX502GW {
}
rogcore.config_mut().current_mode[3] = mode;
if let Some(bytes) = rogcore.config_mut().get_current() {
rogcore.aura_set_and_save(&bytes).unwrap();
rogcore.aura_set_and_save(&bytes)?;
rogcore.config().write();
}
}
@@ -118,7 +136,7 @@ impl Laptop for LaptopGX502GW {
rogcore.suspend();
}
GX502GWKeys::AirplaneMode => {
rogcore.toggle_airplane_mode();
rogcore.toggle_airplane_mode()?;
}
_ => {
if key_byte != 0 {
@@ -126,6 +144,7 @@ impl Laptop for LaptopGX502GW {
}
}
}
Ok(())
}
fn hotkey_group_byte(&self) -> u8 {
self.hotkey_group_byte
@@ -147,6 +166,10 @@ impl Laptop for LaptopGX502GW {
fn prod_family(&self) -> &str {
&self.prod_family
}
fn led_iface_num(&self) -> u8 {
self.led_iface_num
}
}
pub enum GX502GWKeys {