From e21a6e3fb3cec71c731554744b1d4826d7d38523 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 21 Apr 2020 21:07:30 +1200 Subject: [PATCH] Change module/method visibility --- Cargo.lock | 4 +- rog-core/src/daemon.rs | 43 +++++---------- rog-lib/src/core.rs | 106 ++++++++++++++++++++----------------- rog-lib/src/laptops.rs | 92 +++++++++++++++++++------------- rog-lib/src/lib.rs | 6 ++- rog-lib/src/virt_device.rs | 11 ++-- 6 files changed, 137 insertions(+), 125 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cd7329a..f52bc369 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,7 +444,7 @@ checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] name = "rog-core" -version = "0.3.2" +version = "0.3.3" dependencies = [ "dbus", "env_logger 0.7.1", @@ -457,7 +457,7 @@ dependencies = [ [[package]] name = "rog-lib" -version = "0.4.0" +version = "0.4.1" dependencies = [ "aho-corasick 0.7.10", "gumdrop", diff --git a/rog-core/src/daemon.rs b/rog-core/src/daemon.rs index c3186ff1..1ccc6e73 100644 --- a/rog-core/src/daemon.rs +++ b/rog-core/src/daemon.rs @@ -6,19 +6,19 @@ use dbus::{ use log::{error, info, warn}; use rog_lib::{ core::RogCore, - laptops::{match_laptop, Laptop}, + laptops::{match_laptop, LaptopRunner}, }; use std::error::Error; use std::time::Duration; use std::{cell::RefCell, rc::Rc}; -pub struct Daemon { +pub(crate) struct Daemon { rogcore: RogCore, - laptop: Box, + laptop: Box, } impl Daemon { - pub fn new() -> Self { + pub(crate) fn new() -> Self { let laptop = match_laptop(); Daemon { @@ -36,7 +36,7 @@ impl Daemon { } } - pub fn start() -> Result<(), Box> { + pub(crate) fn start() -> Result<(), Box> { let mut connection = Connection::new_system().map_or_else( |err| { error!("{:?}", err); @@ -96,8 +96,6 @@ impl Daemon { // We add the tree to the connection so that incoming method calls will be handled. tree.start_receive(&connection); - let mut key_buf = [0u8; 32]; - let hotkey_group_bytes = Vec::from(daemon.borrow().laptop.hotkey_group_bytes()); loop { connection .process(Duration::from_millis(10)) @@ -105,29 +103,16 @@ impl Daemon { error!("{:?}", err); false }); - // READ KEYBOARD - // TODO: this needs to move to a thread, but there is unsafety - let mut borrowed_daemon = daemon.borrow_mut(); - match borrowed_daemon - .rogcore - .poll_keyboard(&hotkey_group_bytes, &mut key_buf) - { - Ok(read) => { - // Doing this because the Laptop trait takes RogCore, but RogCore contains laptop - // and this makes the borrow checker unhappy, but it's safe for this - let mut rogcore = unsafe { &mut (*daemon.as_ptr()).rogcore }; - if let Some(_count) = read { - borrowed_daemon - .laptop - .do_hotkey_action(&mut rogcore, key_buf[1]) - .unwrap_or_else(|err| { - warn!("{:?}", err); - }); - } - } - Err(err) => error!("{:?}", err), - } + // TODO: this needs to move to a thread, but there is unsafety + let borrowed_daemon = daemon.borrow_mut(); + let mut rogcore = unsafe { &mut (*daemon.as_ptr()).rogcore }; + borrowed_daemon + .laptop + .run(&mut rogcore) + .unwrap_or_else(|err| { + error!("{:?}", err); + }); } } } diff --git a/rog-lib/src/core.rs b/rog-lib/src/core.rs index d18f690b..9ba0a50d 100644 --- a/rog-lib/src/core.rs +++ b/rog-lib/src/core.rs @@ -5,7 +5,7 @@ use crate::{ }; use aho_corasick::AhoCorasick; use gumdrop::Options; -use log::{debug, warn}; +use log::warn; use rusb::DeviceHandle; use std::process::Command; use std::str::FromStr; @@ -35,14 +35,14 @@ static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 pub struct RogCore { handle: DeviceHandle, initialised: bool, - led_iface_num: u8, + led_endpoint: u8, keys_endpoint: u8, config: Config, virt_keys: VirtKeys, } impl RogCore { - pub fn new(laptop: &dyn Laptop) -> Result { + pub fn new(laptop: &dyn LaptopRunner) -> Result { let mut dev_handle = RogCore::get_device(laptop.usb_vendor(), laptop.usb_product())?; dev_handle.set_active_configuration(0).unwrap_or(()); @@ -50,11 +50,10 @@ impl RogCore { // Interface with outputs let mut led_interface_num = 0; let mut keys_interface_num = 0; - let keys_endpoint = 0x83; for iface in dev_config.interfaces() { for desc in iface.descriptors() { for endpoint in desc.endpoint_descriptors() { - if endpoint.address() == keys_endpoint { + if endpoint.address() == laptop.key_iface_num() { keys_interface_num = desc.interface_number(); } else if endpoint.address() == laptop.led_iface_num() { led_interface_num = desc.interface_number(); @@ -72,22 +71,22 @@ impl RogCore { Ok(RogCore { handle: dev_handle, initialised: false, - led_iface_num: led_interface_num, - keys_endpoint, + led_endpoint: led_interface_num, + keys_endpoint: keys_interface_num, config: Config::default().read(), virt_keys: VirtKeys::new(), }) } - pub fn virt_keys(&mut self) -> &mut VirtKeys { + pub(crate) fn virt_keys(&mut self) -> &mut VirtKeys { &mut self.virt_keys } - pub fn config(&self) -> &Config { + pub(crate) fn config(&self) -> &Config { &self.config } - pub fn config_mut(&mut self) -> &mut Config { + pub(crate) fn config_mut(&mut self) -> &mut Config { &mut self.config } @@ -113,7 +112,7 @@ impl RogCore { fn aura_write_messages(&mut self, messages: &[&[u8]]) -> Result<(), AuraError> { self.handle - .claim_interface(self.led_iface_num) + .claim_interface(self.led_endpoint) .map_err(|err| AuraError::UsbError(err))?; if !self.initialised { @@ -133,42 +132,19 @@ impl RogCore { self.aura_write(&LED_APPLY)?; self.handle - .release_interface(self.led_iface_num) + .release_interface(self.led_endpoint) .map_err(|err| AuraError::UsbError(err))?; Ok(()) } - 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; - bright[1] = 0xba; - bright[2] = 0xc5; - bright[3] = 0xc4; - bright[4] = brightness; - Ok(bright) - } - - pub fn aura_set_and_save( + /// Write the bytes read from the device interrupt to the buffer arg, and returns the + /// count of bytes written + /// + /// `report_filter_bytes` is used to filter the data read from the interupt so + /// only the relevant byte array is returned. + pub(crate) fn poll_keyboard( &mut self, - supported_modes: &[BuiltInModeByte], - bytes: &[u8], - ) -> Result<(), AuraError> { - let mode = BuiltInModeByte::from(bytes[3]); - if supported_modes.contains(&mode) || bytes[1] == 0xba { - let messages = [bytes]; - self.aura_write_messages(&messages)?; - self.config.set_field_from(bytes); - self.config.write(); - return Ok(()); - } - warn!("{:?} not supported", BuiltInModeByte::from(mode)); - Err(AuraError::NotSupported) - } - - pub fn poll_keyboard( - &mut self, - hotkey_group_bytes: &[u8], + report_filter_bytes: &[u8], buf: &mut [u8; 32], ) -> Result, AuraError> { let res = @@ -177,7 +153,7 @@ impl RogCore { .read_interrupt(self.keys_endpoint, buf, Duration::from_micros(1)) { Ok(o) => { - if hotkey_group_bytes.contains(&buf[0]) { + if report_filter_bytes.contains(&buf[0]) { Ok(Some(o)) } else { Ok(None) @@ -188,14 +164,22 @@ impl RogCore { res } - pub fn suspend(&self) { + /// A direct call to systemd to suspend the PC. + /// + /// This avoids desktop environments being required to handle it + /// (which means it works while in a TTY also) + pub(crate) fn suspend_with_systemd(&self) { std::process::Command::new("systemctl") .arg("suspend") .spawn() .map_or_else(|err| warn!("Failed to suspend: {}", err), |_| {}); } - pub fn toggle_airplane_mode(&self) { + /// A direct call to rfkill to suspend wireless devices. + /// + /// This avoids desktop environments being required to handle it (which + /// means it works while in a TTY also) + pub(crate) fn toggle_airplane_mode(&self) { match Command::new("rfkill").arg("list").output() { Ok(output) => { if output.status.success() { @@ -229,16 +213,40 @@ impl RogCore { } } } + + pub fn aura_brightness_bytes(brightness: u8) -> Result<[u8; 17], AuraError> { + // TODO: check brightness range + Ok([ + 0x5A, 0xBA, 0xC5, 0xC4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + } + + pub fn aura_set_and_save( + &mut self, + supported_modes: &[BuiltInModeByte], + bytes: &[u8], + ) -> Result<(), AuraError> { + let mode = BuiltInModeByte::from(bytes[3]); + if supported_modes.contains(&mode) || bytes[1] == 0xba { + let messages = [bytes]; + self.aura_write_messages(&messages)?; + self.config.set_field_from(bytes); + self.config.write(); + return Ok(()); + } + warn!("{:?} not supported", BuiltInModeByte::from(mode)); + Err(AuraError::NotSupported) + } } -pub struct Backlight { +pub(crate) struct Backlight { backlight: sysfs_class::Backlight, step: u64, max: u64, } impl Backlight { - pub fn new(id: &str) -> Result { + pub(crate) fn new(id: &str) -> Result { for bl in sysfs_class::Backlight::iter() { let bl = bl?; if bl.id() == id { @@ -253,7 +261,7 @@ impl Backlight { } panic!("Backlight not found") } - pub fn step_up(&self) { + pub(crate) fn step_up(&self) { let brightness = self .backlight .brightness() @@ -268,7 +276,7 @@ impl Backlight { ); } } - pub fn step_down(&self) { + pub(crate) fn step_down(&self) { let brightness = self .backlight .brightness() diff --git a/rog-lib/src/laptops.rs b/rog-lib/src/laptops.rs index 8aa0e945..12c7e789 100644 --- a/rog-lib/src/laptops.rs +++ b/rog-lib/src/laptops.rs @@ -5,7 +5,7 @@ use crate::virt_device::ConsumerKeys; //use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState}; use log::info; -pub fn match_laptop() -> Box { +pub fn match_laptop() -> Box { let dmi = sysfs_class::DmiId::default(); let board_name = dmi.board_name().unwrap(); match board_name.as_str() { @@ -17,25 +17,31 @@ pub fn match_laptop() -> Box { } } -/// All laptop models should implement this trait +/// All laptop models should implement this trait. The role of a `Laptop` is to +/// "drive" the `RogCore`. /// /// `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) -> Result<(), AuraError>; +pub(crate) trait Laptop { fn hotkey_group_bytes(&self) -> &[u8]; - fn led_iface_num(&self) -> u8; - fn supported_modes(&self) -> &[BuiltInModeByte]; - fn usb_vendor(&self) -> u16; - fn usb_product(&self) -> u16; fn board_name(&self) -> &str; fn prod_family(&self) -> &str; } -pub struct LaptopGX502GW { +/// The public interface for running a laptop. Primarily used by the daemon. +pub trait LaptopRunner { + fn run(&self, core: &mut RogCore) -> Result<(), AuraError>; + fn led_iface_num(&self) -> u8; + fn key_iface_num(&self) -> u8; + fn usb_vendor(&self) -> u16; + fn usb_product(&self) -> u16; + fn supported_modes(&self) -> &[BuiltInModeByte]; +} + +pub(crate) struct LaptopGX502GW { usb_vendor: u16, usb_product: u16, board_name: &'static str, @@ -44,6 +50,7 @@ pub struct LaptopGX502GW { min_led_bright: u8, max_led_bright: u8, led_iface_num: u8, + key_iface_num: u8, supported_modes: [BuiltInModeByte; 12], backlight: Backlight, } @@ -60,6 +67,7 @@ impl LaptopGX502GW { min_led_bright: 0x00, max_led_bright: 0x03, led_iface_num: 0x81, + key_iface_num: 0x83, supported_modes: [ BuiltInModeByte::Stable, BuiltInModeByte::Breathe, @@ -78,10 +86,14 @@ impl LaptopGX502GW { } } } -impl Laptop for LaptopGX502GW { + +impl LaptopRunner for LaptopGX502GW { // TODO: This really needs to match against u16 in future - fn do_hotkey_action(&self, rogcore: &mut RogCore, key_byte: u8) -> Result<(), AuraError> { - match GX502GWKeys::from(key_byte) { + fn run(&self, rogcore: &mut RogCore) -> Result<(), AuraError> { + let mut key_buf = [0u8; 32]; + rogcore.poll_keyboard(&self.hotkey_group_bytes, &mut key_buf)?; + + match GX502GWKeys::from(key_buf[1]) { GX502GWKeys::LedBrightUp => { let mut bright = rogcore.config().brightness; if bright < self.max_led_bright { @@ -135,7 +147,7 @@ impl Laptop for LaptopGX502GW { } GX502GWKeys::Sleep => { // Direct call to systemd - rogcore.suspend(); + rogcore.suspend_with_systemd(); //rogcore.virt_keys().press([0x01, 0, 0, 0x82, 0, 0, 0, 0]); // Power menu //rogcore.virt_keys().press([0x01, 0, 0, 0x66, 0, 0, 0, 0]); @@ -160,48 +172,52 @@ impl Laptop for LaptopGX502GW { } GX502GWKeys::None => { - if key_byte != 0 { + if key_buf[1] != 0 { info!( - "Unmapped key, attempt to pass to virtual device: {:?}, {:X?}", - &key_byte, &key_byte + "Unmapped key, attempt to pass to virtual device: {:X?}", + &key_buf[1] ); let mut bytes = [0u8; 8]; // TODO: code page bytes[0] = 0x02; - bytes[1] = key_byte; + bytes[1] = key_buf[1]; rogcore.virt_keys().press(bytes); } } } Ok(()) } - fn hotkey_group_bytes(&self) -> &[u8] { - &self.hotkey_group_bytes - } - fn supported_modes(&self) -> &[BuiltInModeByte] { - &self.supported_modes - } - fn usb_vendor(&self) -> u16 { - self.usb_vendor - } - - fn usb_product(&self) -> u16 { - self.usb_product - } - fn board_name(&self) -> &str { - &self.board_name - } - - fn prod_family(&self) -> &str { - &self.prod_family - } fn led_iface_num(&self) -> u8 { self.led_iface_num } + fn key_iface_num(&self) -> u8 { + self.key_iface_num + } + fn usb_vendor(&self) -> u16 { + self.usb_vendor + } + fn usb_product(&self) -> u16 { + self.usb_product + } + fn supported_modes(&self) -> &[BuiltInModeByte] { + &self.supported_modes + } } -pub enum GX502GWKeys { +impl Laptop for LaptopGX502GW { + fn hotkey_group_bytes(&self) -> &[u8] { + &self.hotkey_group_bytes + } + fn board_name(&self) -> &str { + &self.board_name + } + fn prod_family(&self) -> &str { + &self.prod_family + } +} + +pub(crate) enum GX502GWKeys { Rog = 0x38, MicToggle = 0x7C, Fan = 0xAE, diff --git a/rog-lib/src/lib.rs b/rog-lib/src/lib.rs index a7c600bb..75b85c71 100644 --- a/rog-lib/src/lib.rs +++ b/rog-lib/src/lib.rs @@ -1,7 +1,9 @@ -pub mod aura; +mod aura; /// Contains mostly only what is required for parsing CLI options pub mod cli_options; -pub mod config; +mod config; +/// The core module which allows writing to LEDs or polling the +/// laptop keyboard attached devices pub mod core; mod error; pub mod laptops; diff --git a/rog-lib/src/virt_device.rs b/rog-lib/src/virt_device.rs index 5397ae15..1497cada 100644 --- a/rog-lib/src/virt_device.rs +++ b/rog-lib/src/virt_device.rs @@ -1,11 +1,11 @@ use uhid_virt::{Bus, CreateParams, UHIDDevice}; -pub struct VirtKeys { - pub device: UHIDDevice, +pub(crate) struct VirtKeys { + device: UHIDDevice, } impl VirtKeys { - pub fn new() -> Self { + pub(crate) fn new() -> Self { VirtKeys { device: UHIDDevice::create(CreateParams { name: String::from("Virtual ROG buttons"), @@ -71,7 +71,7 @@ impl VirtKeys { } } - pub fn press(&mut self, input: [u8; 8]) { + pub(crate) fn press(&mut self, input: [u8; 8]) { self.device.write(&input).unwrap(); let mut reset = [0u8; 8]; reset[0] = input[0]; @@ -79,8 +79,9 @@ impl VirtKeys { } } +#[allow(dead_code)] #[derive(Copy, Clone)] -pub enum ConsumerKeys { +pub(crate) enum ConsumerKeys { Power = 0x30, Sleep = 0x32, Menu = 0x0040,