Files
asusctl/src/core.rs
2020-04-16 08:14:58 +12:00

143 lines
4.5 KiB
Rust

use crate::aura::AuraError;
use crate::aura::ModeMessage;
use gumdrop::Options;
use rusb::{DeviceHandle, Error};
use std::str::FromStr;
use std::time::Duration;
pub const LED_MSG_LEN: usize = 17;
static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
static LED_INIT2: &'static str = "]ASUS Tech.Inc."; // ] == 0x5d
static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];
static LED_INIT4: &'static 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];
#[derive(Debug, Options)]
pub struct LedBrightness {
pub level: u8,
}
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:
///
/// -` LED_INIT1`
/// - `LED_INIT3`
/// - `LED_INIT4`
/// - `LED_INIT2`
/// - `LED_INIT4`
pub struct RogCore {
handle: DeviceHandle<rusb::GlobalContext>,
initialised: bool,
led_interface_num: u8,
keys_interface_num: u8,
}
impl RogCore {
pub fn new() -> Result<RogCore, Error> {
let mut handle = RogCore::get_device(0x0B05, 0x1866)?;
handle.set_active_configuration(0).unwrap_or(());
let config = handle.device().config_descriptor(0).unwrap();
// Interface with outputs
let mut keys_interface_num = 0;
let mut led_interface_num = 0;
for iface in config.interfaces() {
for desc in iface.descriptors() {
for endpoint in desc.endpoint_descriptors() {
if endpoint.address() == 0x83 {
keys_interface_num = desc.interface_number();
} else if endpoint.address() == 0x81 {
led_interface_num = desc.interface_number();
}
}
}
}
handle.set_auto_detach_kernel_driver(true).unwrap();
handle.set_auto_detach_kernel_driver(true).unwrap();
Ok(RogCore {
handle,
initialised: false,
led_interface_num,
keys_interface_num,
})
}
fn get_device(vendor: u16, product: u16) -> Result<DeviceHandle<rusb::GlobalContext>, Error> {
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();
}
}
Err(Error::NoDevice)
}
fn aura_write_messages(&mut self, messages: &[[u8; LED_MSG_LEN]]) -> Result<(), Error> {
self.handle.claim_interface(self.led_interface_num)?;
// Declared as a zoomy so that it is hidden
let write = |message: &[u8]| {
self.handle
.write_control(0x21, 0x09, 0x035D, 0, message, Duration::new(0, 5))
};
if !self.initialised {
write(&LED_INIT1)?;
write(LED_INIT2.as_bytes())?;
write(&LED_INIT3)?;
write(LED_INIT4.as_bytes())?;
write(&LED_INIT5)?;
}
for message in messages {
println!("{:x?}", &message);
write(message)?;
write(&LED_SET)?;
}
// Changes won't persist unless apply is set
write(&LED_APPLY)?;
self.handle.release_interface(self.led_interface_num)?;
Ok(())
}
pub fn aura_set_brightness(&mut self, brightness: u8) -> Result<(), Error> {
let mut bright = [0u8; LED_MSG_LEN];
bright[0] = 0x5a;
bright[1] = 0xba;
bright[2] = 0xc5;
bright[3] = 0xc4;
bright[4] = brightness;
let messages = [bright];
self.aura_write_messages(&messages)
}
pub fn aura_set_mode(&mut self, mode: ModeMessage) -> Result<(), Error> {
let messages = [mode.0];
self.aura_write_messages(&messages)
}
}