Create rog-platform, refactor rogcc ipc-file handling

- Create new rog-platform crate to manage all i/o in a universal way
  + kbd-led handling
  + platform handling (asus-nb-wmi)
  + hidraw
  + usbraw
- Refactor how ROGCC handles IPC for background open, run-in-bg
This commit is contained in:
Luke D. Jones
2022-08-12 15:22:06 +12:00
parent 45268bfb2b
commit 308fba9413
31 changed files with 860 additions and 635 deletions

12
rog-platform/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "rog_platform"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "*"
udev = "^0.6"
rusb = "^0.9"
sysfs-class = "^0.1"

51
rog-platform/src/error.rs Normal file
View File

@@ -0,0 +1,51 @@
use std::fmt;
pub type Result<T> = std::result::Result<T, PlatformError>;
#[derive(Debug)]
pub enum PlatformError {
ParseVendor,
ParseNum,
Udev(String, std::io::Error),
USB(rusb::Error),
Path(String, std::io::Error),
Read(String, std::io::Error),
Write(String, std::io::Error),
NotSupported,
AttrNotFound(String),
MissingFunction(String),
MissingLedBrightNode(String, std::io::Error),
Io(String, std::io::Error),
NoAuraKeyboard,
NoAuraNode,
}
impl fmt::Display for PlatformError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PlatformError::ParseVendor => write!(f, "Parse gfx vendor error"),
PlatformError::ParseNum => write!(f, "Parse number error"),
PlatformError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
PlatformError::USB(error) => write!(f, "usb {}", error),
PlatformError::Path(path, error) => write!(f, "Path {}: {}", path, error),
PlatformError::Read(path, error) => write!(f, "Read {}: {}", path, error),
PlatformError::Write(path, error) => write!(f, "Write {}: {}", path, error),
PlatformError::NotSupported => write!(f, "Not supported"),
PlatformError::AttrNotFound(deets) => write!(f, "Attribute not found: {}", deets),
PlatformError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
PlatformError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
PlatformError::Io(path, detail) => write!(f, "std::io error: {} {}", path, detail),
PlatformError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
PlatformError::NoAuraNode => write!(f, "No Aura keyboard node found"),
}
}
}
impl std::error::Error for PlatformError {}
impl From<rusb::Error> for PlatformError {
fn from(err: rusb::Error) -> Self {
PlatformError::USB(err)
}
}

View File

@@ -0,0 +1,55 @@
use std::{fs::OpenOptions, io::Write, path::PathBuf};
use log::{info, warn};
use crate::error::{PlatformError, Result};
#[derive(Debug, PartialEq, PartialOrd)]
pub struct HidRaw(PathBuf);
impl HidRaw {
pub fn new(id_product: &str) -> Result<Self> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator
.scan_devices()
.map_err(|e| PlatformError::Io("enumerator".to_owned(), e))?
{
if let Some(parent) = device
.parent_with_subsystem_devtype("usb", "usb_device")
.map_err(|e| PlatformError::Io(device.devpath().to_string_lossy().to_string(), e))?
{
if let Some(parent) = parent.attribute_value("idProduct") {
if parent == id_product {
if let Some(dev_node) = device.devnode() {
info!("Using device at: {:?} for LED control", dev_node);
return Ok(Self(dev_node.to_owned()));
}
}
}
}
}
Err(PlatformError::MissingFunction(format!(
"hidraw dev {} not found",
id_product
)))
}
pub fn write_bytes(&self, message: &[u8]) -> Result<()> {
let mut file = OpenOptions::new()
.write(true)
.open(&self.0)
.map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e))?;
// println!("write: {:02x?}", &message);
file.write_all(message)
.map_err(|e| PlatformError::Io(self.0.to_string_lossy().to_string(), e))
}
}

View File

@@ -0,0 +1,59 @@
use std::path::PathBuf;
use log::warn;
use crate::{
attr_u8, attr_u8_array,
error::{PlatformError, Result},
to_device,
};
#[derive(Debug, Default, PartialEq, PartialOrd)]
pub struct KeyboardLed(PathBuf);
impl KeyboardLed {
pub fn new() -> Result<Self> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("leds").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
enumerator
.match_sysname("asus::kbd_backlight")
.map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("scan_devices failed".into(), err)
})? {
return Ok(Self(device.syspath().to_owned()));
}
Err(PlatformError::MissingFunction(
"asus::kbd_backlight not found".into(),
))
}
attr_u8!(has_brightness, get_brightness, set_brightness, "brightness");
attr_u8_array!(
has_keyboard_rgb_mode,
get_keyboard_rgb_mode,
set_keyboard_rgb_mode,
"kbd_rgb_mode"
);
attr_u8_array!(
has_keyboard_rgb_state,
get_keyboard_rgb_state,
set_keyboard_rgb_state,
"kbd_rgb_state"
);
}

191
rog-platform/src/lib.rs Normal file
View File

@@ -0,0 +1,191 @@
pub mod error;
pub mod hid_raw;
pub mod keyboard_led;
pub mod platform;
pub mod usb_raw;
use std::path::Path;
use error::{PlatformError, Result};
use udev::Device;
#[macro_export]
macro_rules! attr_bool {
($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => {
pub fn $hasser(&self) -> bool {
match to_device(&self.0) {
Ok(p) => crate::has_attr(&p, $attr_name),
Err(_) => false,
}
}
pub fn $getter(&self) -> Result<bool> {
crate::read_attr_bool(&to_device(&self.0)?, $attr_name)
}
pub fn $setter(&self, value: bool) -> Result<()> {
crate::write_attr_bool(&mut to_device(&self.0)?, $attr_name, value)
}
};
}
#[macro_export]
macro_rules! attr_u8 {
($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => {
pub fn $hasser(&self) -> bool {
match to_device(&self.0) {
Ok(p) => crate::has_attr(&p, $attr_name),
Err(_) => false,
}
}
pub fn $getter(&self) -> Result<u8> {
crate::read_attr_u8(&to_device(&self.0)?, $attr_name)
}
pub fn $setter(&self, value: u8) -> Result<()> {
crate::write_attr_u8(&mut to_device(&self.0)?, $attr_name, value)
}
};
}
#[macro_export]
macro_rules! attr_u8_array {
($hasser:ident, $getter:ident, $setter:ident, $attr_name:literal) => {
pub fn $hasser(&self) -> bool {
match to_device(&self.0) {
Ok(p) => crate::has_attr(&p, $attr_name),
Err(_) => false,
}
}
pub fn $getter(&self) -> Result<Vec<u8>> {
crate::read_attr_u8_array(&to_device(&self.0)?, $attr_name)
}
pub fn $setter(&self, values: &[u8]) -> Result<()> {
crate::write_attr_u8_array(&mut to_device(&self.0)?, $attr_name, values)
}
};
}
pub(crate) fn to_device(sys_path: &Path) -> Result<Device> {
Device::from_syspath(sys_path)
.map_err(|e| PlatformError::Udev("Couldn't transform syspath to device".to_string(), e))
}
pub fn has_attr(device: &Device, attr_name: &str) -> bool {
for attr in device.attributes() {
if attr.name() == attr_name {
return true;
}
}
false
}
pub fn read_attr_bool(device: &Device, attr_name: &str) -> Result<bool> {
if let Some(value) = device.attribute_value(attr_name) {
let tmp = value.to_string_lossy();
if tmp.trim() == "0" {
return Ok(false);
}
return Ok(true);
}
Err(PlatformError::AttrNotFound(attr_name.to_string()))
}
pub fn write_attr_bool(device: &mut Device, attr: &str, value: bool) -> Result<()> {
device
.set_attribute_value(attr, &(value as u8).to_string())
.map_err(|e| PlatformError::Io(attr.into(), e))
}
pub fn read_attr_u8(device: &Device, attr_name: &str) -> Result<u8> {
if let Some(value) = device.attribute_value(attr_name) {
let tmp = value.to_string_lossy();
return Ok(tmp.parse::<u8>().map_err(|_| PlatformError::ParseNum)?);
}
Err(PlatformError::AttrNotFound(attr_name.to_string()))
}
pub fn write_attr_u8(device: &mut Device, attr: &str, value: u8) -> Result<()> {
device
.set_attribute_value(attr, &(value).to_string())
.map_err(|e| PlatformError::Io(attr.into(), e))
}
pub fn read_attr_u8_array(device: &Device, attr_name: &str) -> Result<Vec<u8>> {
if let Some(value) = device.attribute_value(attr_name) {
let tmp = value.to_string_lossy();
let tmp = tmp
.split(' ')
.map(|v| u8::from_str_radix(v, 10).unwrap_or(0))
.collect();
return Ok(tmp);
}
Err(PlatformError::AttrNotFound(attr_name.to_string()))
}
pub fn write_attr_u8_array(device: &mut Device, attr: &str, values: &[u8]) -> Result<()> {
let tmp: String = values.iter().map(|v| format!("{} ", v)).collect();
let tmp = tmp.trim();
device
.set_attribute_value(attr, &tmp)
.map_err(|e| PlatformError::Io(attr.into(), e))
}
#[cfg(test)]
mod tests {
#[test]
fn check() {
let data = [1, 2, 3, 4, 5];
let tmp: String = data.iter().map(|v| format!("{} ", v)).collect();
let tmp = tmp.trim();
assert_eq!(tmp, "1 2 3 4 5");
let tmp: Vec<u8> = tmp
.split(' ')
.map(|v| u8::from_str_radix(v, 10).unwrap_or(0))
.collect();
assert_eq!(tmp, &[1, 2, 3, 4, 5]);
}
}
// pub fn find_led_node(id_product: &str) -> Result<Device, PlatformError> {
// let mut enumerator = udev::Enumerator::new().map_err(|err| {
// warn!("{}", err);
// PlatformError::Udev("enumerator failed".into(), err)
// })?;
// enumerator.match_subsystem("hidraw").map_err(|err| {
// warn!("{}", err);
// PlatformError::Udev("match_subsystem failed".into(), err)
// })?;
// for device in enumerator.scan_devices().map_err(|err| {
// warn!("{}", err);
// PlatformError::Udev("scan_devices failed".into(), err)
// })? {
// if let Some(parent) = device
// .parent_with_subsystem_devtype("usb", "usb_device")
// .map_err(|err| {
// warn!("{}", err);
// PlatformError::Udev("parent_with_subsystem_devtype failed".into(), err)
// })?
// {
// if parent
// .attribute_value("idProduct")
// .ok_or_else(|| PlatformError::NotFound("LED idProduct".into()))?
// == id_product
// {
// if let Some(dev_node) = device.devnode() {
// info!("Using device at: {:?} for LED control", dev_node);
// return Ok(device);
// }
// }
// }
// }
// Err(PlatformError::MissingFunction(
// "ASUS LED device node not found".into(),
// ))
// }

View File

@@ -0,0 +1,69 @@
use std::path::PathBuf;
use log::warn;
use crate::{
attr_bool,
error::{PlatformError, Result},
to_device,
};
/// The "platform" device provides access to things like:
/// - dgpu_disable
/// - egpu_enable
/// - panel_od
/// - dgpu_only
/// - keyboard_mode, set keyboard RGB mode and speed
/// - keyboard_state, set keyboard power states
#[derive(Debug, PartialEq, PartialOrd)]
pub struct AsusPlatform(PathBuf);
impl AsusPlatform {
pub fn new() -> Result<Self> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("platform").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
enumerator.match_sysname("asus-nb-wmi").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("scan_devices failed".into(), err)
})? {
return Ok(Self(device.syspath().to_owned()));
}
Err(PlatformError::MissingFunction(
"asus-nb-wmi not found".into(),
))
}
attr_bool!(
has_dgpu_disable,
get_dgpu_disable,
set_dgpu_disable,
"dgpu_disable"
);
attr_bool!(
has_egpu_enable,
get_egpu_enable,
set_egpu_enable,
"egpu_enable"
);
attr_bool!(has_panel_od, get_panel_od, set_panel_od, "panel_od");
attr_bool!(
has_gpu_mux_mode,
get_gpu_mux_mode,
set_gpu_mux_mode,
"gpu_mux_mode"
);
}

View File

@@ -0,0 +1,48 @@
use rusb::{Device, DeviceHandle};
use std::time::Duration;
use crate::error::{PlatformError, Result};
#[derive(Debug, PartialEq)]
pub struct USBRaw(DeviceHandle<rusb::GlobalContext>);
impl USBRaw {
pub fn new(id_product: u16) -> Result<Self> {
for device in rusb::devices()?.iter() {
let device_desc = device.device_descriptor()?;
if device_desc.vendor_id() == 0x0b05 && device_desc.product_id() == id_product {
let handle = Self::get_dev_handle(device)?;
return Ok(Self(handle));
}
}
Err(PlatformError::MissingFunction(format!(
"USBRaw dev {} not found",
id_product
)))
}
fn get_dev_handle(
device: Device<rusb::GlobalContext>,
) -> Result<DeviceHandle<rusb::GlobalContext>> {
// We don't expect this ID to ever change
let mut device = device.open()?;
device.reset()?;
device.set_auto_detach_kernel_driver(true)?;
device.claim_interface(0)?;
Ok(device)
}
pub fn write_bytes(&self, message: &[u8]) -> Result<usize> {
self.0
.write_control(
0x21, // request_type
0x09, // request
0x35e, // value
0x00, // index
message,
Duration::from_millis(200),
)
.map_err(|e| PlatformError::USB(e))
}
}