Attempt to support GA502DU, GL753VE. Bump version

This commit is contained in:
Luke
2020-04-23 14:51:20 +12:00
parent 7b3c2acd5e
commit 11548217b7
10 changed files with 292 additions and 47 deletions

2
Cargo.lock generated
View File

@@ -397,7 +397,7 @@ checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
[[package]]
name = "rog-daemon"
version = "0.4.1"
version = "0.5.0"
dependencies = [
"aho-corasick",
"dbus",

View File

@@ -1,6 +1,6 @@
[package]
name = "rog-daemon"
version = "0.4.1"
version = "0.5.0"
authors = ["Luke <luke@ljones.dev>"]
edition = "2018"

View File

@@ -2,6 +2,8 @@
rog-core is a utility for Linux to control many aspects (eventually) of the ASUS ROG laptops like the Zephyrus GX502GW.
One of the benefits of this app (for me at least) is that you *don't* require a kernel with correct support for the laptop, or custom patched modules. The app reads and writes direct to the device interrupts, and can be customised (in source) quite extensively to do what you want such as directly controlling your laptop backlight rather than emitting a key-press for the DE to handle. There is also the possibility of rebinding fn keys to be macros which emit a series of keyboard presses.
The laptop I currently have is the GX502RW and so I'll be using that for the basis of this app. If I get wireshark captures from others with different ROG laptops then I should be able to add them.
I'm now looking at the kernel source to see if I can add the inputs correctly so they show up as proper evdev events.
@@ -73,6 +75,17 @@ Currently if no options are supplied for the CLI mode selection then a default i
As the daemon currently stands it should be enough for a functional system.
## Other Laptops
**Supported:**
- GX502 (Tested on GX502GW)
**Please help test or provide info for:**
- GA502 (attempts to use same profile as GX502GW)
- GL753 (attempted support from researching 2nd-hand info, multizone may work)
## Wireshark captures
TODO: see `./wireshark_data/` for some captures.

View File

@@ -46,6 +46,9 @@ pub fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
///
/// Bytes 0 and 1 should always be 5d, b3
///
/// On multizone laptops byte 2 is the zone number, RGB in usual
/// place, byte 3 set to zero
///
/// Byte 3 sets the mode type:
/// - 00 = static
/// - 01 = breathe (can set two colours)
@@ -126,6 +129,7 @@ impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
SetAuraBuiltin::Pulse(_) => msg[3] = 0x0a,
SetAuraBuiltin::ThinZoomy(_) => msg[3] = 0x0b,
SetAuraBuiltin::WideZoomy(_) => msg[3] = 0x0c,
_ => panic!("Mode not convertable to array"),
}
match mode {
@@ -163,6 +167,37 @@ impl From<SetAuraBuiltin> for [u8; LED_MSG_LEN] {
msg[5] = settings.colour.1;
msg[6] = settings.colour.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
}
impl From<SetAuraBuiltin> for [[u8; LED_MSG_LEN]; 4] {
fn from(mode: SetAuraBuiltin) -> Self {
let mut msg = [[0u8; LED_MSG_LEN]; 4];
for i in 0..4 {
msg[i][0] = 0x5d;
msg[i][1] = 0xb3;
msg[i][2] = i as u8 + 1;
}
match mode {
SetAuraBuiltin::MultiStatic(settings) => {
msg[0][4] = settings.colour1.0;
msg[0][5] = settings.colour1.1;
msg[0][6] = settings.colour1.2;
msg[1][4] = settings.colour2.0;
msg[1][5] = settings.colour2.1;
msg[1][6] = settings.colour2.2;
msg[2][4] = settings.colour3.0;
msg[2][5] = settings.colour3.1;
msg[2][6] = settings.colour3.2;
msg[3][4] = settings.colour4.0;
msg[3][5] = settings.colour4.1;
msg[3][6] = settings.colour4.2;
}
_ => panic!("Mode not convertable to array"),
}
msg
}
@@ -184,6 +219,7 @@ pub struct BuiltInModeBytes {
pub pulse: [u8; LED_MSG_LEN],
pub thinzoomy: [u8; LED_MSG_LEN],
pub widezoomy: [u8; LED_MSG_LEN],
pub multi_static: [[u8; LED_MSG_LEN]; 4],
}
impl BuiltInModeBytes {
pub fn set_field_from(&mut self, bytes: &[u8]) {
@@ -249,6 +285,9 @@ impl Default for BuiltInModeBytes {
widezoomy: <[u8; LED_MSG_LEN]>::from(
SetAuraBuiltin::WideZoomy(SingleColour::default()),
),
multi_static: <[[u8; LED_MSG_LEN]; 4]>::from(SetAuraBuiltin::MultiStatic(
MultiColour::default(),
)),
}
}
}

View File

@@ -107,6 +107,20 @@ pub struct SingleColour {
pub colour: Colour,
}
#[derive(Debug, Default, Options)]
pub struct MultiColour {
#[options(help = "print help message")]
help: bool,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour1: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour2: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour3: Colour,
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
pub colour4: Colour,
}
#[derive(Debug, Default, Options)]
pub struct SingleSpeedDirection {
#[options(help = "print help message")]
@@ -160,6 +174,8 @@ pub enum SetAuraBuiltin {
ThinZoomy(SingleColour),
#[options(help = "set a wide vertical line zooming from left")]
WideZoomy(SingleColour),
#[options(help = "4-zone multi-colour")]
MultiStatic(MultiColour),
}
impl Default for SetAuraBuiltin {

View File

@@ -272,6 +272,7 @@ impl RogCore {
&mut self,
supported_modes: &[BuiltInModeByte],
) -> Result<(), AuraError> {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = self.config.current_mode[3];
let idx = supported_modes.binary_search(&mode_curr.into()).unwrap();
let idx_next = if idx < supported_modes.len() - 1 {
@@ -295,6 +296,7 @@ impl RogCore {
&mut self,
supported_modes: &[BuiltInModeByte],
) -> Result<(), AuraError> {
// TODO: different path for multi-zone (byte 2 controlled, non-zero)
let mode_curr = self.config.current_mode[3];
let idx = supported_modes.binary_search(&mode_curr.into()).unwrap();
let idx_next = if idx > 0 {

146
src/laptops/gl753.rs Normal file
View File

@@ -0,0 +1,146 @@
use crate::aura::BuiltInModeByte;
use crate::core::{Backlight, RogCore};
use crate::error::AuraError;
use crate::virt_device::ConsumerKeys;
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
use super::Laptop;
use log::info;
pub(super) struct LaptopGL753 {
usb_vendor: u16,
usb_product: u16,
board_name: &'static str,
prod_family: &'static str,
report_filter_bytes: [u8; 2],
min_led_bright: u8,
max_led_bright: u8,
led_endpoint: u8,
key_endpoint: u8,
supported_modes: [BuiltInModeByte; 3],
backlight: Backlight,
}
impl LaptopGL753 {
pub(super) fn new() -> Self {
LaptopGL753 {
usb_vendor: 0x0B05,
usb_product: 0x1854,
// from `cat /sys/class/dmi/id/board_name`
board_name: "LaptopGL753VE",
// from `cat /sys/class/dmi/id/product_family`
prod_family: "ROG Strix",
report_filter_bytes: [0x5a, 0x02],
min_led_bright: 0x00,
max_led_bright: 0x03,
//from `lsusb -vd 0b05:1866`
led_endpoint: 0x04,
//from `lsusb -vd 0b05:1866`
key_endpoint: 0x83,
supported_modes: [
BuiltInModeByte::Stable,
BuiltInModeByte::Breathe,
BuiltInModeByte::Cycle,
],
backlight: Backlight::new("intel_backlight").unwrap(),
}
}
}
impl LaptopGL753 {
fn do_keypress_actions(&self, rogcore: &mut RogCore) -> Result<(), AuraError> {
if let Some(key_buf) = rogcore.poll_keyboard(&self.report_filter_bytes) {
match GL753Keys::from(key_buf[1]) {
GL753Keys::LedBrightUp => {
rogcore.aura_bright_inc(&self.supported_modes, self.max_led_bright)?;
}
GL753Keys::LedBrightDown => {
rogcore.aura_bright_dec(&self.supported_modes, self.min_led_bright)?;
}
GL753Keys::ScreenBrightUp => self.backlight.step_up(),
GL753Keys::ScreenBrightDown => self.backlight.step_down(),
GL753Keys::Sleep => rogcore.suspend_with_systemd(),
GL753Keys::AirplaneMode => rogcore.toggle_airplane_mode(),
GL753Keys::ScreenToggle => {
rogcore.virt_keys().press(ConsumerKeys::BacklightTog.into());
}
GL753Keys::TouchPadToggle => {
let mut key = [0u8; 32];
key[0] = 0x01;
key[3] = 0x070;
rogcore.virt_keys().press(key);
}
GL753Keys::Rog => {
let mut key = [0u8; 32];
key[0] = 0x01;
key[3] = 0x68; // XF86Tools? F13
rogcore.virt_keys().press(key);
}
GL753Keys::None => {
if key_buf[0] != 0x5A {
info!("Unmapped key array: {:X?}", &key_buf);
info!("Attempting passthrough: {:X?}", &key_buf[1]);
rogcore.virt_keys().press(key_buf);
}
}
}
}
Ok(())
}
}
impl Laptop for LaptopGL753 {
fn run(&self, rogcore: &mut RogCore) -> Result<(), AuraError> {
self.do_keypress_actions(rogcore)
}
fn led_endpoint(&self) -> u8 {
self.led_endpoint
}
fn key_endpoint(&self) -> u8 {
self.key_endpoint
}
fn usb_vendor(&self) -> u16 {
self.usb_vendor
}
fn usb_product(&self) -> u16 {
self.usb_product
}
fn supported_modes(&self) -> &[BuiltInModeByte] {
&self.supported_modes
}
fn board_name(&self) -> &str {
&self.board_name
}
fn prod_family(&self) -> &str {
&self.prod_family
}
}
enum GL753Keys {
Rog = 0x38,
ScreenToggle = 0x35,
ScreenBrightDown = 0x10,
ScreenBrightUp = 0x20,
TouchPadToggle = 0x6b,
Sleep = 0x6C,
AirplaneMode = 0x88,
LedBrightUp = 0xC4,
LedBrightDown = 0xC5,
None,
}
impl From<u8> for GL753Keys {
fn from(byte: u8) -> Self {
match byte {
0x38 => GL753Keys::Rog,
0x35 => GL753Keys::ScreenToggle,
0x10 => GL753Keys::ScreenBrightDown,
0x20 => GL753Keys::ScreenBrightUp,
0x6b => GL753Keys::TouchPadToggle,
0x6C => GL753Keys::Sleep,
0x88 => GL753Keys::AirplaneMode,
0xC4 => GL753Keys::LedBrightUp,
0xC5 => GL753Keys::LedBrightDown,
_ => GL753Keys::None,
}
}
}

View File

@@ -6,7 +6,7 @@ use crate::virt_device::ConsumerKeys;
use super::Laptop;
use log::info;
pub(super) struct LaptopGX502GW {
pub(super) struct LaptopGX502 {
usb_vendor: u16,
usb_product: u16,
board_name: &'static str,
@@ -21,7 +21,7 @@ pub(super) struct LaptopGX502GW {
per_key_led: Vec<KeyColourArray>,
}
impl LaptopGX502GW {
impl LaptopGX502 {
pub(super) fn new() -> Self {
// Setting up a sample effect to run when ROG pressed
let mut per_key_led = Vec::new();
@@ -56,7 +56,7 @@ impl LaptopGX502GW {
}
// Find backlight
LaptopGX502GW {
LaptopGX502 {
usb_vendor: 0x0B05,
usb_product: 0x1866,
// from `cat /sys/class/dmi/id/board_name`
@@ -90,34 +90,34 @@ impl LaptopGX502GW {
}
}
impl LaptopGX502GW {
impl LaptopGX502 {
fn do_keypress_actions(&self, rogcore: &mut RogCore) -> Result<(), AuraError> {
if let Some(key_buf) = rogcore.poll_keyboard(&self.report_filter_bytes) {
match GX502GWKeys::from(key_buf[1]) {
GX502GWKeys::LedBrightUp => {
match GX502Keys::from(key_buf[1]) {
GX502Keys::LedBrightUp => {
rogcore.aura_bright_inc(&self.supported_modes, self.max_led_bright)?;
}
GX502GWKeys::LedBrightDown => {
GX502Keys::LedBrightDown => {
rogcore.aura_bright_dec(&self.supported_modes, self.min_led_bright)?;
}
GX502GWKeys::AuraNext => rogcore.aura_mode_next(&self.supported_modes)?,
GX502GWKeys::AuraPrevious => rogcore.aura_mode_prev(&self.supported_modes)?,
GX502GWKeys::ScreenBrightUp => self.backlight.step_up(),
GX502GWKeys::ScreenBrightDown => self.backlight.step_down(),
GX502GWKeys::Sleep => rogcore.suspend_with_systemd(),
GX502GWKeys::AirplaneMode => rogcore.toggle_airplane_mode(),
GX502GWKeys::MicToggle => {}
GX502GWKeys::Fan => {}
GX502GWKeys::ScreenToggle => {
GX502Keys::AuraNext => rogcore.aura_mode_next(&self.supported_modes)?,
GX502Keys::AuraPrevious => rogcore.aura_mode_prev(&self.supported_modes)?,
GX502Keys::ScreenBrightUp => self.backlight.step_up(),
GX502Keys::ScreenBrightDown => self.backlight.step_down(),
GX502Keys::Sleep => rogcore.suspend_with_systemd(),
GX502Keys::AirplaneMode => rogcore.toggle_airplane_mode(),
GX502Keys::MicToggle => {}
GX502Keys::Fan => {}
GX502Keys::ScreenToggle => {
rogcore.virt_keys().press(ConsumerKeys::BacklightTog.into());
}
GX502GWKeys::TouchPadToggle => {
GX502Keys::TouchPadToggle => {
let mut key = [0u8; 32];
key[0] = 0x01;
key[3] = 0x070;
rogcore.virt_keys().press(key);
}
GX502GWKeys::Rog => {
GX502Keys::Rog => {
rogcore.aura_effect_init()?;
rogcore.aura_write_effect(&self.per_key_led)?;
// let mut key = [0u8; 32];
@@ -125,7 +125,7 @@ impl LaptopGX502GW {
// key[3] = 0x68; // XF86Tools? F13
// rogcore.virt_keys().press(key);
}
GX502GWKeys::None => {
GX502Keys::None => {
if key_buf[0] != 0x5A {
info!("Unmapped key, attempt passthrough: {:X?}", &key_buf[1]);
rogcore.virt_keys().press(key_buf);
@@ -137,7 +137,7 @@ impl LaptopGX502GW {
}
}
impl Laptop for LaptopGX502GW {
impl Laptop for LaptopGX502 {
fn run(&self, rogcore: &mut RogCore) -> Result<(), AuraError> {
self.do_keypress_actions(rogcore)
}
@@ -164,7 +164,7 @@ impl Laptop for LaptopGX502GW {
}
}
enum GX502GWKeys {
enum GX502Keys {
Rog = 0x38,
MicToggle = 0x7C,
Fan = 0xAE,
@@ -181,23 +181,23 @@ enum GX502GWKeys {
None,
}
impl From<u8> for GX502GWKeys {
impl From<u8> for GX502Keys {
fn from(byte: u8) -> Self {
match byte {
0x38 => GX502GWKeys::Rog,
0x7C => GX502GWKeys::MicToggle,
0xAE => GX502GWKeys::Fan,
0x35 => GX502GWKeys::ScreenToggle,
0x10 => GX502GWKeys::ScreenBrightDown,
0x20 => GX502GWKeys::ScreenBrightUp,
0x6b => GX502GWKeys::TouchPadToggle,
0x6C => GX502GWKeys::Sleep,
0x88 => GX502GWKeys::AirplaneMode,
0xC4 => GX502GWKeys::LedBrightUp,
0xC5 => GX502GWKeys::LedBrightDown,
0xB2 => GX502GWKeys::AuraPrevious,
0xB3 => GX502GWKeys::AuraNext,
_ => GX502GWKeys::None,
0x38 => GX502Keys::Rog,
0x7C => GX502Keys::MicToggle,
0xAE => GX502Keys::Fan,
0x35 => GX502Keys::ScreenToggle,
0x10 => GX502Keys::ScreenBrightDown,
0x20 => GX502Keys::ScreenBrightUp,
0x6b => GX502Keys::TouchPadToggle,
0x6C => GX502Keys::Sleep,
0x88 => GX502Keys::AirplaneMode,
0xC4 => GX502Keys::LedBrightUp,
0xC5 => GX502Keys::LedBrightDown,
0xB2 => GX502Keys::AuraPrevious,
0xB3 => GX502Keys::AuraNext,
_ => GX502Keys::None,
}
}
}

View File

@@ -4,17 +4,28 @@ use crate::error::AuraError;
//use keycode::{KeyMap, KeyMappingId, KeyState, KeyboardState};
use log::info;
mod gx502gw;
use gx502gw::LaptopGX502GW;
// GL753VE == 0x1854, 4 zone keyboard
mod gl753;
use gl753::LaptopGL753;
// 0x1866, per-key LEDs, media-keys split from vendor specific
mod gx502;
use gx502::LaptopGX502;
pub(crate) fn match_laptop() -> Box<dyn Laptop> {
let dmi = sysfs_class::DmiId::default();
let board_name = dmi.board_name().unwrap();
match board_name.as_str() {
// The hell does it have a \n for anyway?
"GX502GW\n" => {
info!("Found GX502GW");
Box::new(LaptopGX502GW::new())
match &board_name.as_str()[..5] {
// TODO: FX503VD - 0x1869,
// These two models seem to have the same characteristics
"GX502" | "GA502" => {
info!("Found GX502 or GA502 series");
Box::new(LaptopGX502::new())
}
// LED should work for this, but unsure of keys
"GL753" => {
info!("Found GL753 series");
Box::new(LaptopGL753::new())
}
_ => {
panic!("could not match laptop");

View File

@@ -10,10 +10,14 @@ use env_logger::{Builder, Target};
use gumdrop::Options;
use log::LevelFilter;
static VERSION: &'static str = "0.5.0";
#[derive(Debug, Options)]
struct CLIStart {
#[options(help = "print help message")]
help: bool,
#[options(help = "show program version number")]
version: bool,
#[options(help = "start daemon")]
daemon: bool,
#[options(meta = "VAL", help = "<off, low, med, high>")]
@@ -46,12 +50,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if parsed.daemon {
start_daemon()?;
}
if parsed.version {
println!("Version: {}", VERSION);
}
match parsed.command {
Some(Command::LedMode(mode)) => {
if let Some(command) = mode.command {
let mode = <[u8; LED_MSG_LEN]>::from(command);
dbus_led_builtin_write(&mode)?;
// Check for special modes here, eg, per-key or multi-zone
match command {
SetAuraBuiltin::MultiStatic(_) => {
let byte_arr = <[[u8; LED_MSG_LEN]; 4]>::from(command);
for arr in byte_arr.iter() {
dbus_led_builtin_write(arr)?;
}
}
_ => {
let mode = <[u8; LED_MSG_LEN]>::from(command);
dbus_led_builtin_write(&mode)?;
}
}
}
}
None => {}