Refactor: Make all Aura type devices use "device_manager"

Open the door to adding many other types of "aura" devices later.
This commit is contained in:
Luke D. Jones
2024-11-04 08:55:37 +01:00
parent 0ddfe76c31
commit 19ffcf3376
48 changed files with 2349 additions and 2240 deletions

View File

@@ -4,6 +4,9 @@
### Changed ### Changed
- Fix attribute writes. At some point the kernel API seems to have changed. - Fix attribute writes. At some point the kernel API seems to have changed.
- Extremely large refactor of Aura device handling. Should enable easy add of different kinds now.
- Add GA605W LED layout
- Rename CLI args for aura related properties. This will likely change further as more devices are added
## [v6.0.12] ## [v6.0.12]

41
Cargo.lock generated
View File

@@ -930,7 +930,7 @@ dependencies = [
[[package]] [[package]]
name = "const-field-offset" name = "const-field-offset"
version = "0.1.5" version = "0.1.5"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"const-field-offset-macro", "const-field-offset-macro",
"field-offset", "field-offset",
@@ -939,7 +939,7 @@ dependencies = [
[[package]] [[package]]
name = "const-field-offset-macro" name = "const-field-offset-macro"
version = "0.1.5" version = "0.1.5"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2151,7 +2151,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-backend-linuxkms" name = "i-slint-backend-linuxkms"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"calloop 0.14.2", "calloop 0.14.2",
"drm", "drm",
@@ -2169,19 +2169,20 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-backend-selector" name = "i-slint-backend-selector"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"i-slint-backend-linuxkms", "i-slint-backend-linuxkms",
"i-slint-backend-winit", "i-slint-backend-winit",
"i-slint-common", "i-slint-common",
"i-slint-core", "i-slint-core",
"i-slint-core-macros",
] ]
[[package]] [[package]]
name = "i-slint-backend-winit" name = "i-slint-backend-winit"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"ashpd", "ashpd",
"cfg-if", "cfg-if",
@@ -2212,7 +2213,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-common" name = "i-slint-common"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"derive_more", "derive_more",
@@ -2224,7 +2225,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-compiler" name = "i-slint-compiler"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"by_address", "by_address",
"codemap", "codemap",
@@ -2254,7 +2255,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-core" name = "i-slint-core"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"auto_enums", "auto_enums",
"bitflags 2.6.0", "bitflags 2.6.0",
@@ -2299,7 +2300,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-core-macros" name = "i-slint-core-macros"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"quote", "quote",
"serde_json", "serde_json",
@@ -2309,7 +2310,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-renderer-femtovg" name = "i-slint-renderer-femtovg"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"const-field-offset", "const-field-offset",
@@ -2339,7 +2340,7 @@ dependencies = [
[[package]] [[package]]
name = "i-slint-renderer-skia" name = "i-slint-renderer-skia"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
@@ -2831,9 +2832,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.168" version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@@ -4546,7 +4547,7 @@ dependencies = [
[[package]] [[package]]
name = "slint" name = "slint"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"const-field-offset", "const-field-offset",
"i-slint-backend-selector", "i-slint-backend-selector",
@@ -4563,7 +4564,7 @@ dependencies = [
[[package]] [[package]]
name = "slint-build" name = "slint-build"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"i-slint-compiler", "i-slint-compiler",
"i-slint-core-macros", "i-slint-core-macros",
@@ -4575,7 +4576,7 @@ dependencies = [
[[package]] [[package]]
name = "slint-macros" name = "slint-macros"
version = "1.9.0" version = "1.9.0"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"i-slint-compiler", "i-slint-compiler",
"proc-macro2", "proc-macro2",
@@ -5535,8 +5536,8 @@ dependencies = [
[[package]] [[package]]
name = "vtable" name = "vtable"
version = "0.2.0" version = "0.2.1"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"const-field-offset", "const-field-offset",
"portable-atomic", "portable-atomic",
@@ -5546,8 +5547,8 @@ dependencies = [
[[package]] [[package]]
name = "vtable-macro" name = "vtable-macro"
version = "0.2.0" version = "0.2.1"
source = "git+https://github.com/slint-ui/slint.git#dc4bda958ebfda6184825bdacf6cb0bbd6fc7bd2" source = "git+https://github.com/slint-ui/slint.git#09c9857082ff0e5cef6cdb4ed4396aab9eafb9d4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -19,17 +19,22 @@ LEDCFG := aura_support.ron
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs') SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
STRIP_BINARIES ?= 0 STRIP_BINARIES ?= 1
DEBUG ?= 0 DEBUG ?= 0
ifeq ($(DEBUG),0) ifeq ($(DEBUG),0)
ARGS += --release --features "rog-control-center/x11" ARGS += --release
TARGET = release TARGET = release
else else
ARGS += --profile dev --features "rog-control-center/x11" ARGS += --profile dev
TARGET = debug TARGET = debug
endif endif
X11 ?= 0
ifeq ($(X11),1)
ARGS += --features "rog-control-center/x11"
endif
VENDORED ?= 0 VENDORED ?= 0
ifeq ($(VENDORED),1) ifeq ($(VENDORED),1)
ARGS += --frozen ARGS += --frozen
@@ -133,7 +138,7 @@ ifeq ($(VENDORED),1)
tar pxf vendor_asusctl_$(VERSION).tar.xz tar pxf vendor_asusctl_$(VERSION).tar.xz
endif endif
cargo build $(ARGS) cargo build $(ARGS)
ifneq ($(STRIP_BINARIES),0) ifeq ($(STRIP_BINARIES),1)
strip -s ./target/$(TARGET)/$(BIN_C) strip -s ./target/$(TARGET)/$(BIN_C)
strip -s ./target/$(TARGET)/$(BIN_D) strip -s ./target/$(TARGET)/$(BIN_D)
strip -s ./target/$(TARGET)/$(BIN_U) strip -s ./target/$(TARGET)/$(BIN_U)

View File

@@ -3,7 +3,7 @@ use std::error::Error;
use std::path::Path; use std::path::Path;
use std::process::exit; use std::process::exit;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType}; use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -26,7 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
AnimeType::GA401, AnimeType::GA401,
)?; )?;
let anime_type = get_maybe_anime_type()?; let anime_type = get_anime_type();
proxy.write(matrix.into_data_buffer(anime_type)?).unwrap(); proxy.write(matrix.into_data_buffer(anime_type)?).unwrap();

View File

@@ -1,7 +1,7 @@
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDiagonal, AnimeType}; use rog_anime::{AnimeDiagonal, AnimeType};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -29,7 +29,7 @@ fn main() {
} }
} }
let anime_type = get_maybe_anime_type().unwrap(); let anime_type = get_anime_type();
proxy proxy
.write(matrix.into_data_buffer(anime_type).unwrap()) .write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap(); .unwrap();

View File

@@ -2,7 +2,7 @@ use std::env;
use std::path::Path; use std::path::Path;
use std::thread::sleep; use std::thread::sleep;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{ActionData, ActionLoader, Sequences}; use rog_anime::{ActionData, ActionLoader, Sequences};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -19,7 +19,7 @@ fn main() {
let path = Path::new(&args[1]); let path = Path::new(&args[1]);
let brightness = args[2].parse::<f32>().unwrap(); let brightness = args[2].parse::<f32>().unwrap();
let anime_type = get_maybe_anime_type().unwrap(); let anime_type = get_anime_type();
let mut seq = Sequences::new(anime_type); let mut seq = Sequences::new(anime_type);
seq.insert( seq.insert(
0, 0,

View File

@@ -1,6 +1,6 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeGrid}; use rog_anime::{AnimeDataBuffer, AnimeGrid};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -14,7 +14,7 @@ fn main() {
let conn = Connection::system().unwrap(); let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap(); let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_maybe_anime_type().unwrap(); let anime_type = get_anime_type();
let mut matrix = AnimeGrid::new(anime_type); let mut matrix = AnimeGrid::new(anime_type);
let tmp = matrix.get_mut(); let tmp = matrix.get_mut();

View File

@@ -1,4 +1,4 @@
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::AnimeDataBuffer; use rog_anime::AnimeDataBuffer;
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -9,7 +9,7 @@ use zbus::blocking::Connection;
fn main() { fn main() {
let conn = Connection::system().unwrap(); let conn = Connection::system().unwrap();
let proxy = AnimeProxyBlocking::new(&conn).unwrap(); let proxy = AnimeProxyBlocking::new(&conn).unwrap();
let anime_type = get_maybe_anime_type().unwrap(); let anime_type = get_anime_type();
let mut matrix = AnimeDataBuffer::new(anime_type); let mut matrix = AnimeDataBuffer::new(anime_type);
matrix.data_mut()[1] = 100; // start = 1 matrix.data_mut()[1] = 100; // start = 1
for n in matrix.data_mut()[2..32].iter_mut() { for n in matrix.data_mut()[2..32].iter_mut() {

View File

@@ -4,7 +4,7 @@ use std::error::Error;
use std::path::Path; use std::path::Path;
use std::process::exit; use std::process::exit;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2}; use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_maybe_anime_type()?; let anime_type = get_anime_type();
let matrix = AnimeImage::from_png( let matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),

View File

@@ -7,7 +7,7 @@ use std::process::exit;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2}; use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2};
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
use zbus::blocking::Connection; use zbus::blocking::Connection;
@@ -23,7 +23,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1); exit(-1);
} }
let anime_type = get_maybe_anime_type()?; let anime_type = get_anime_type();
let mut matrix = AnimeImage::from_png( let mut matrix = AnimeImage::from_png(
Path::new(&args[1]), Path::new(&args[1]),
args[2].parse::<f32>().unwrap(), args[2].parse::<f32>().unwrap(),

View File

@@ -31,11 +31,11 @@ pub struct CliStart {
#[derive(Options)] #[derive(Options)]
pub enum CliCommand { pub enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")] #[options(help = "Set the keyboard lighting from built-in modes")]
LedMode(LedModeCommand), Aura(LedModeCommand),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
LedPow1(LedPowerCommand1), AuraPowerOld(LedPowerCommand1),
#[options(help = "Set the LED power states")] #[options(help = "Set the LED power states")]
LedPow2(LedPowerCommand2), AuraPower(LedPowerCommand2),
#[options(help = "Set or select platform_profile")] #[options(help = "Set or select platform_profile")]
Profile(ProfileCommand), Profile(ProfileCommand),
#[options(help = "Set, select, or modify fan curves if supported")] #[options(help = "Set, select, or modify fan curves if supported")]
@@ -47,7 +47,7 @@ pub enum CliCommand {
#[options(name = "slash", help = "Manage Slash Ledbar")] #[options(name = "slash", help = "Manage Slash Ledbar")]
Slash(SlashCommand), Slash(SlashCommand),
#[options(help = "Change bios settings")] #[options(help = "Change bios settings")]
Bios(BiosCommand), Platform(PlatformCommand),
} }
#[derive(Debug, Clone, Options)] #[derive(Debug, Clone, Options)]
@@ -87,7 +87,7 @@ pub struct GraphicsCommand {
} }
#[derive(Options, Debug)] #[derive(Options, Debug)]
pub struct BiosCommand { pub struct PlatformCommand {
#[options(help = "print help message")] #[options(help = "print help message")]
pub help: bool, pub help: bool,
#[options( #[options(

View File

@@ -9,7 +9,7 @@ use aura_cli::{LedPowerCommand1, LedPowerCommand2};
use dmi_id::DMIID; use dmi_id::DMIID;
use fan_curve_cli::FanCurveCommand; use fan_curve_cli::FanCurveCommand;
use gumdrop::{Opt, Options}; use gumdrop::{Opt, Options};
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2}; use rog_anime::{AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, Vec2};
use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower}; use rog_aura::keyboard::{AuraPowerState, LaptopAuraPower};
use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones}; use rog_aura::{self, AuraDeviceType, AuraEffect, PowerZones};
@@ -23,6 +23,7 @@ use rog_platform::platform::{GpuMode, Properties, ThrottlePolicy};
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use rog_slash::SlashMode; use rog_slash::SlashMode;
use ron::ser::PrettyConfig; use ron::ser::PrettyConfig;
use zbus::blocking::proxy::ProxyImpl;
use zbus::blocking::Connection; use zbus::blocking::Connection;
use crate::aura_cli::{AuraPowerStates, LedBrightness}; use crate::aura_cli::{AuraPowerStates, LedBrightness};
@@ -122,31 +123,33 @@ fn check_service(name: &str) -> bool {
false false
} }
fn find_aura_iface() -> Result<Vec<AuraProxyBlocking<'static>>, Box<dyn std::error::Error>> { fn find_iface<T>(iface_name: &str) -> Result<Vec<T>, Box<dyn std::error::Error>>
where
T: ProxyImpl<'static> + From<zbus::Proxy<'static>>,
{
let conn = zbus::blocking::Connection::system().unwrap(); let conn = zbus::blocking::Connection::system().unwrap();
let f = let f =
zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "org.asuslinux.Daemon", "/").unwrap(); zbus::blocking::fdo::ObjectManagerProxy::new(&conn, "org.asuslinux.Daemon", "/").unwrap();
let interfaces = f.get_managed_objects().unwrap(); let interfaces = f.get_managed_objects().unwrap();
let mut aura_paths = Vec::new(); let mut paths = Vec::new();
for v in interfaces.iter() { for v in interfaces.iter() {
// let o: Vec<zbus::names::OwnedInterfaceName> = v.1.keys().map(|e| // let o: Vec<zbus::names::OwnedInterfaceName> = v.1.keys().map(|e|
// e.to_owned()).collect(); println!("{}, {:?}", v.0, o); // e.to_owned()).collect(); println!("{}, {:?}", v.0, o);
for k in v.1.keys() { for k in v.1.keys() {
if k.as_str() == "org.asuslinux.Aura" { if k.as_str() == iface_name {
println!("Found aura device at {}, {}", v.0, k); println!("Found {iface_name} device at {}, {}", v.0, k);
aura_paths.push(v.0.clone()); paths.push(v.0.clone());
} }
} }
} }
if aura_paths.len() > 1 { if paths.len() > 1 {
println!("Multiple aura devices found: {aura_paths:?}"); println!("Multiple aura devices found: {paths:?}");
println!("TODO: enable selection");
} }
if !aura_paths.is_empty() { if !paths.is_empty() {
let mut ctrl = Vec::new(); let mut ctrl = Vec::new();
for path in aura_paths { for path in paths {
ctrl.push( ctrl.push(
AuraProxyBlocking::builder(&conn) T::builder(&conn)
.path(path.clone())? .path(path.clone())?
.destination("org.asuslinux.Daemon")? .destination("org.asuslinux.Daemon")?
.build()?, .build()?,
@@ -165,9 +168,9 @@ fn do_parsed(
conn: Connection, conn: Connection,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
match &parsed.command { match &parsed.command {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&find_aura_iface()?, mode)?, Some(CliCommand::Aura(mode)) => handle_led_mode(mode)?,
Some(CliCommand::LedPow1(pow)) => handle_led_power1(&find_aura_iface()?, pow)?, Some(CliCommand::AuraPowerOld(pow)) => handle_led_power1(pow)?,
Some(CliCommand::LedPow2(pow)) => handle_led_power2(&find_aura_iface()?, pow)?, Some(CliCommand::AuraPower(pow)) => handle_led_power2(pow)?,
Some(CliCommand::Profile(cmd)) => { Some(CliCommand::Profile(cmd)) => {
handle_throttle_profile(&conn, supported_properties, cmd)? handle_throttle_profile(&conn, supported_properties, cmd)?
} }
@@ -175,9 +178,9 @@ fn do_parsed(
handle_fan_curve(&conn, cmd)?; handle_fan_curve(&conn, cmd)?;
} }
Some(CliCommand::Graphics(_)) => do_gfx(), Some(CliCommand::Graphics(_)) => do_gfx(),
Some(CliCommand::Anime(cmd)) => handle_anime(&conn, cmd)?, Some(CliCommand::Anime(cmd)) => handle_anime(cmd)?,
Some(CliCommand::Slash(cmd)) => handle_slash(&conn, cmd)?, Some(CliCommand::Slash(cmd)) => handle_slash(cmd)?,
Some(CliCommand::Bios(cmd)) => { Some(CliCommand::Platform(cmd)) => {
handle_platform_properties(&conn, supported_properties, cmd)? handle_platform_properties(&conn, supported_properties, cmd)?
} }
None => { None => {
@@ -192,7 +195,8 @@ fn do_parsed(
println!("{}", CliStart::usage()); println!("{}", CliStart::usage());
println!(); println!();
if let Some(cmdlist) = CliStart::command_list() { if let Some(cmdlist) = CliStart::command_list() {
let dev_type = if let Ok(proxy) = find_aura_iface() { let dev_type =
if let Ok(proxy) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
// TODO: commands on all? // TODO: commands on all?
proxy proxy
.first() .first()
@@ -211,6 +215,12 @@ fn do_parsed(
return false; return false;
} }
if command.trim().starts_with("aura")
&& !supported_interfaces.contains(&"org.asuslinux.Aura".to_string())
{
return false;
}
if command.trim().starts_with("anime") if command.trim().starts_with("anime")
&& !supported_interfaces.contains(&"org.asuslinux.Anime".to_string()) && !supported_interfaces.contains(&"org.asuslinux.Anime".to_string())
{ {
@@ -223,7 +233,7 @@ fn do_parsed(
return false; return false;
} }
if command.trim().starts_with("bios") if command.trim().starts_with("platform")
&& !supported_interfaces.contains(&"org.asuslinux.Platform".to_string()) && !supported_interfaces.contains(&"org.asuslinux.Platform".to_string())
{ {
return false; return false;
@@ -231,11 +241,11 @@ fn do_parsed(
if !dev_type.is_old_laptop() if !dev_type.is_old_laptop()
&& !dev_type.is_tuf_laptop() && !dev_type.is_tuf_laptop()
&& command.trim().starts_with("led-pow-1") && command.trim().starts_with("aura-power-old")
{ {
return false; return false;
} }
if !dev_type.is_new_laptop() && command.trim().starts_with("led-pow-2") { if !dev_type.is_new_laptop() && command.trim().starts_with("aura-power") {
return false; return false;
} }
true true
@@ -252,7 +262,7 @@ fn do_parsed(
} }
if let Some(brightness) = &parsed.kbd_bright { if let Some(brightness) = &parsed.kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
match brightness.level() { match brightness.level() {
None => { None => {
@@ -268,7 +278,7 @@ fn do_parsed(
} }
if parsed.next_kbd_bright { if parsed.next_kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
let brightness = aura.brightness()?; let brightness = aura.brightness()?;
aura.set_brightness(brightness.next())?; aura.set_brightness(brightness.next())?;
@@ -279,7 +289,7 @@ fn do_parsed(
} }
if parsed.prev_kbd_bright { if parsed.prev_kbd_bright {
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
for aura in aura.iter() { for aura in aura.iter() {
let brightness = aura.brightness()?; let brightness = aura.brightness()?;
aura.set_brightness(brightness.prev())?; aura.set_brightness(brightness.prev())?;
@@ -295,7 +305,7 @@ fn do_parsed(
"Supported Platform Properties:\n{:#?}", "Supported Platform Properties:\n{:#?}",
supported_properties supported_properties
); );
if let Ok(aura) = find_aura_iface() { if let Ok(aura) = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura") {
// TODO: multiple RGB check // TODO: multiple RGB check
let bright = aura.first().unwrap().supported_brightness()?; let bright = aura.first().unwrap().supported_brightness()?;
let modes = aura.first().unwrap().supported_basic_modes()?; let modes = aura.first().unwrap().supported_basic_modes()?;
@@ -331,7 +341,7 @@ fn do_gfx() {
println!("This command will be removed in future"); println!("This command will be removed in future");
} }
fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_anime(cmd: &AnimeCommand) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.command.is_none() if (cmd.command.is_none()
&& cmd.enable_display.is_none() && cmd.enable_display.is_none()
&& cmd.enable_powersave_anim.is_none() && cmd.enable_powersave_anim.is_none()
@@ -348,7 +358,8 @@ fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std
println!("\n{}", lst); println!("\n{}", lst);
} }
} }
let proxy = AnimeProxyBlocking::new(conn)?; let animes = find_iface::<AnimeProxyBlocking>("org.asuslinux.Anime")?;
for proxy in animes {
if let Some(enable) = cmd.enable_display { if let Some(enable) = cmd.enable_display {
proxy.set_enable_display(enable)?; proxy.set_enable_display(enable)?;
} }
@@ -371,7 +382,7 @@ fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std
println!("Did Alice _really_ make it back from Wonderland?"); println!("Did Alice _really_ make it back from Wonderland?");
} }
let mut anime_type = get_maybe_anime_type()?; let mut anime_type = get_anime_type();
if let AnimeType::Unsupported = anime_type { if let AnimeType::Unsupported = anime_type {
if let Some(model) = cmd.override_type { if let Some(model) = cmd.override_type {
anime_type = model; anime_type = model;
@@ -493,7 +504,9 @@ fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std
} }
AnimeActions::SetBuiltins(builtins) => { AnimeActions::SetBuiltins(builtins) => {
if builtins.help_requested() || builtins.set.is_none() { if builtins.help_requested() || builtins.set.is_none() {
println!("\nAny unspecified args will be set to default (first shown var)\n"); println!(
"\nAny unspecified args will be set to default (first shown var)\n"
);
println!("\n{}", builtins.self_usage()); println!("\n{}", builtins.self_usage());
if let Some(lst) = builtins.self_command_list() { if let Some(lst) = builtins.self_command_list() {
println!("\n{}", lst); println!("\n{}", lst);
@@ -510,6 +523,7 @@ fn handle_anime(conn: &Connection, cmd: &AnimeCommand) -> Result<(), Box<dyn std
} }
} }
} }
}
Ok(()) Ok(())
} }
@@ -522,7 +536,7 @@ fn verify_brightness(brightness: f32) {
} }
} }
fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> { fn handle_slash(cmd: &SlashCommand) -> Result<(), Box<dyn std::error::Error>> {
if (cmd.brightness.is_none() if (cmd.brightness.is_none()
&& cmd.interval.is_none() && cmd.interval.is_none()
&& cmd.slash_mode.is_none() && cmd.slash_mode.is_none()
@@ -536,7 +550,9 @@ fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std
println!("\n{}", lst); println!("\n{}", lst);
} }
} }
let proxy = SlashProxyBlocking::new(conn)?;
let slashes = find_iface::<SlashProxyBlocking>("org.asuslinux.Slash")?;
for proxy in slashes {
if cmd.enable { if cmd.enable {
proxy.set_enabled(true)?; proxy.set_enabled(true)?;
} }
@@ -552,6 +568,7 @@ fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std
if let Some(slash_mode) = cmd.slash_mode { if let Some(slash_mode) = cmd.slash_mode {
proxy.set_slash_mode(slash_mode)?; proxy.set_slash_mode(slash_mode)?;
} }
}
if cmd.list { if cmd.list {
let res = SlashMode::list(); let res = SlashMode::list();
for p in &res { for p in &res {
@@ -562,10 +579,7 @@ fn handle_slash(conn: &Connection, cmd: &SlashCommand) -> Result<(), Box<dyn std
Ok(()) Ok(())
} }
fn handle_led_mode( fn handle_led_mode(mode: &LedModeCommand) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking],
mode: &LedModeCommand,
) -> Result<(), Box<dyn std::error::Error>> {
if mode.command.is_none() && !mode.prev_mode && !mode.next_mode { if mode.command.is_none() && !mode.prev_mode && !mode.next_mode {
if !mode.help { if !mode.help {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
@@ -576,6 +590,7 @@ fn handle_led_mode(
if let Some(cmdlist) = LedModeCommand::command_list() { if let Some(cmdlist) = LedModeCommand::command_list() {
let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect(); let commands: Vec<String> = cmdlist.lines().map(|s| s.to_owned()).collect();
// TODO: multiple rgb check // TODO: multiple rgb check
let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
let modes = aura.first().unwrap().supported_basic_modes()?; let modes = aura.first().unwrap().supported_basic_modes()?;
for command in commands.iter().filter(|command| { for command in commands.iter().filter(|command| {
for mode in &modes { for mode in &modes {
@@ -605,6 +620,7 @@ fn handle_led_mode(
println!("Please specify either next or previous"); println!("Please specify either next or previous");
return Ok(()); return Ok(());
} }
let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
if mode.next_mode { if mode.next_mode {
for aura in aura { for aura in aura {
let mode = aura.led_mode()?; let mode = aura.led_mode()?;
@@ -640,10 +656,8 @@ fn handle_led_mode(
Ok(()) Ok(())
} }
fn handle_led_power1( fn handle_led_power1(power: &LedPowerCommand1) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking], let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
power: &LedPowerCommand1,
) -> Result<(), Box<dyn std::error::Error>> {
for aura in aura { for aura in aura {
let dev_type = aura.device_type()?; let dev_type = aura.device_type()?;
if !dev_type.is_old_laptop() && !dev_type.is_tuf_laptop() { if !dev_type.is_old_laptop() && !dev_type.is_tuf_laptop() {
@@ -664,7 +678,7 @@ fn handle_led_power1(
} }
if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() { if dev_type.is_old_laptop() || dev_type.is_tuf_laptop() {
handle_led_power_1_do_1866(aura, power)?; handle_led_power_1_do_1866(&aura, power)?;
return Ok(()); return Ok(());
} }
} }
@@ -702,10 +716,8 @@ fn handle_led_power_1_do_1866(
Ok(()) Ok(())
} }
fn handle_led_power2( fn handle_led_power2(power: &LedPowerCommand2) -> Result<(), Box<dyn std::error::Error>> {
aura: &[AuraProxyBlocking], let aura = find_iface::<AuraProxyBlocking>("org.asuslinux.Aura")?;
power: &LedPowerCommand2,
) -> Result<(), Box<dyn std::error::Error>> {
for aura in aura { for aura in aura {
let dev_type = aura.device_type()?; let dev_type = aura.device_type()?;
if !dev_type.is_new_laptop() { if !dev_type.is_new_laptop() {
@@ -894,7 +906,7 @@ fn handle_fan_curve(
fn handle_platform_properties( fn handle_platform_properties(
conn: &Connection, conn: &Connection,
supported: &[Properties], supported: &[Properties],
cmd: &BiosCommand, cmd: &PlatformCommand,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
{ {
if (cmd.gpu_mux_mode_set.is_none() if (cmd.gpu_mux_mode_set.is_none()
@@ -907,7 +919,10 @@ fn handle_platform_properties(
{ {
println!("Missing arg or command\n"); println!("Missing arg or command\n");
let usage: Vec<String> = BiosCommand::usage().lines().map(|s| s.to_owned()).collect(); let usage: Vec<String> = PlatformCommand::usage()
.lines()
.map(|s| s.to_owned())
.collect();
for line in usage.iter().filter(|line| { for line in usage.iter().filter(|line| {
line.contains("sound") && supported.contains(&Properties::PostAnimationSound) line.contains("sound") && supported.contains(&Properties::PostAnimationSound)

View File

@@ -11,9 +11,9 @@ pub struct SlashCommand {
pub disable: bool, pub disable: bool,
#[options(meta = "", help = "Set brightness value <0-255>")] #[options(meta = "", help = "Set brightness value <0-255>")]
pub brightness: Option<u8>, pub brightness: Option<u8>,
#[options(meta = "", help = "Set interval value <0-255>")] #[options(meta = "", help = "Set interval value <0-5>")]
pub interval: Option<u8>, pub interval: Option<u8>,
#[options(help = "Set SlashMode (so 'list' for all options)")] #[options(meta = "", help = "Set SlashMode (so 'list' for all options)")]
pub slash_mode: Option<SlashMode>, pub slash_mode: Option<SlashMode>,
#[options(help = "list available animations")] #[options(help = "list available animations")]
pub list: bool, pub list: bool,

View File

@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
use asusd_user::config::*; use asusd_user::config::*;
use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner}; use asusd_user::ctrl_anime::{CtrlAnime, CtrlAnimeInner};
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use rog_anime::usb::get_maybe_anime_type; use rog_anime::usb::get_anime_type;
use rog_aura::aura_detection::LedSupportData; use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::KeyLayout; use rog_aura::keyboard::KeyLayout;
use rog_dbus::zbus_anime::AnimeProxyBlocking; use rog_dbus::zbus_anime::AnimeProxyBlocking;
@@ -44,7 +44,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set up the anime data and run loop/thread // Set up the anime data and run loop/thread
if supported.contains(&"org.asuslinux.Anime".to_string()) { if supported.contains(&"org.asuslinux.Anime".to_string()) {
if let Some(cfg) = config.active_anime { if let Some(cfg) = config.active_anime {
let anime_type = get_maybe_anime_type()?; let anime_type = get_anime_type();
let anime_config = ConfigAnime::new().set_name(cfg).load(); let anime_config = ConfigAnime::new().set_name(cfg).load();
let anime = anime_config.create(anime_type)?; let anime = anime_config.create(anime_type)?;
let anime_config = Arc::new(Mutex::new(anime_config)); let anime_config = Arc::new(Mutex::new(anime_config));

View File

@@ -10,18 +10,18 @@ use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "anime.ron"; const CONFIG_FILE: &str = "anime.ron";
#[derive(Deserialize, Serialize, Default)] #[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AnimeConfigCached { pub struct AniMeConfigCached {
pub system: Vec<ActionData>, pub system: Vec<ActionData>,
pub boot: Vec<ActionData>, pub boot: Vec<ActionData>,
pub wake: Vec<ActionData>, pub wake: Vec<ActionData>,
pub shutdown: Vec<ActionData>, pub shutdown: Vec<ActionData>,
} }
impl AnimeConfigCached { impl AniMeConfigCached {
pub fn init_from_config( pub fn init_from_config(
&mut self, &mut self,
config: &AnimeConfig, config: &AniMeConfig,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
let mut sys = Vec::with_capacity(config.system.len()); let mut sys = Vec::with_capacity(config.system.len());
@@ -53,7 +53,9 @@ impl AnimeConfigCached {
/// Config for base system actions for the anime display /// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug, Clone)] #[derive(Deserialize, Serialize, Debug, Clone)]
pub struct AnimeConfig { pub struct AniMeConfig {
#[serde(skip)]
pub anime_type: AnimeType,
pub system: Vec<ActionLoader>, pub system: Vec<ActionLoader>,
pub boot: Vec<ActionLoader>, pub boot: Vec<ActionLoader>,
pub wake: Vec<ActionLoader>, pub wake: Vec<ActionLoader>,
@@ -69,9 +71,10 @@ pub struct AnimeConfig {
pub builtin_anims: Animations, pub builtin_anims: Animations,
} }
impl Default for AnimeConfig { impl Default for AniMeConfig {
fn default() -> Self { fn default() -> Self {
AnimeConfig { AniMeConfig {
anime_type: AnimeType::GA402,
system: Vec::new(), system: Vec::new(),
boot: Vec::new(), boot: Vec::new(),
wake: Vec::new(), wake: Vec::new(),
@@ -89,7 +92,7 @@ impl Default for AnimeConfig {
} }
} }
impl StdConfig for AnimeConfig { impl StdConfig for AniMeConfig {
fn new() -> Self { fn new() -> Self {
Self::create_default() Self::create_default()
} }
@@ -103,10 +106,10 @@ impl StdConfig for AnimeConfig {
} }
} }
impl StdConfigLoad for AnimeConfig {} impl StdConfigLoad for AniMeConfig {}
impl From<&AnimeConfig> for DeviceState { impl From<&AniMeConfig> for DeviceState {
fn from(config: &AnimeConfig) -> Self { fn from(config: &AniMeConfig) -> Self {
DeviceState { DeviceState {
display_enabled: config.display_enabled, display_enabled: config.display_enabled,
display_brightness: config.display_brightness, display_brightness: config.display_brightness,
@@ -120,7 +123,7 @@ impl From<&AnimeConfig> for DeviceState {
} }
} }
impl AnimeConfig { impl AniMeConfig {
// fn clamp_config_brightness(mut config: &mut AnimeConfig) { // fn clamp_config_brightness(mut config: &mut AnimeConfig) {
// if config.brightness < 0.0 || config.brightness > 1.0 { // if config.brightness < 0.0 || config.brightness > 1.0 {
// warn!( // warn!(
@@ -133,7 +136,7 @@ impl AnimeConfig {
fn create_default() -> Self { fn create_default() -> Self {
// create a default config here // create a default config here
AnimeConfig { AniMeConfig {
system: vec![], system: vec![],
boot: vec![ActionLoader::ImageAnimation { boot: vec![ActionLoader::ImageAnimation {
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(), file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),

247
asusd/src/aura_anime/mod.rs Normal file
View File

@@ -0,0 +1,247 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use config_traits::StdConfig;
use log::{error, info, warn};
use rog_anime::usb::{
pkt_flush, pkt_set_brightness, pkt_set_enable_display, pkt_set_enable_powersave_anim,
pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use tokio::sync::{Mutex, MutexGuard};
use self::config::{AniMeConfig, AniMeConfigCached};
use crate::error::RogError;
#[derive(Debug, Clone)]
pub struct AniMe {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
cache: AniMeConfigCached,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl AniMe {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<AniMeConfig>>,
) -> Self {
Self {
hid,
usb,
config,
cache: AniMeConfigCached::default(),
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
}
}
/// Will fail if something is already holding the config lock
async fn do_init_cache(&mut self) {
if let Ok(mut config) = self.config.try_lock() {
if let Err(e) = self.cache.init_from_config(&config, config.anime_type) {
error!(
"Trying to cache the Anime Config failed, will reset to default config: {e:?}"
);
config.rename_file_old();
*config = AniMeConfig::new();
config.write();
}
} else {
error!("AniMe Matrix could not init cache")
}
}
/// Initialise the device if required.
pub async fn do_initialization(&mut self) -> Result<(), RogError> {
self.do_init_cache().await;
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]).await?;
self.write_bytes(&pkts[1]).await
}
pub async fn lock_config(&self) -> MutexGuard<AniMeConfig> {
self.config.lock().await
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
async fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.write_bytes(row).await?;
}
self.write_bytes(&pkt_flush()).await
}
pub async fn set_builtins_enabled(
&self,
enabled: bool,
bright: Brightness,
) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await?;
self.write_bytes(&pkt_set_enable_display(enabled)).await?;
self.write_bytes(&pkt_set_brightness(bright)).await?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
.await
}
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(&self, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
self.write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
let thread_exit = self.thread_exit.clone();
let thread_running = self.thread_running.clone();
let anime_type = self.config.lock().await.anime_type;
let inner = self.clone();
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
tokio::spawn(async move {
info!("AniMe new system thread started");
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
// TODO: sort all this out
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
let inner = inner.clone();
tokio::task::spawn_local(async move {
inner
.write_data_buffer(frame)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
});
Ok(false) // Don't exit yet
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
inner
.write_data_buffer(image.as_ref().clone())
.await
.map_err(|e| error!("{}", e))
.ok();
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
inner
.write_data_buffer(data)
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
inner
.write_bytes(&pkt_set_enable_powersave_anim(
inner.config.lock().await.builtin_anims_enabled,
))
.await
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.await
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
}

View File

@@ -1,25 +1,22 @@
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use config_traits::StdConfig; use config_traits::StdConfig;
use log::warn; use log::{error, warn};
use logind_zbus::manager::ManagerProxy; use logind_zbus::manager::ManagerProxy;
use rog_anime::usb::{ use rog_anime::usb::{
pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display, pkt_set_brightness, pkt_set_builtin_animations, pkt_set_enable_display,
pkt_set_enable_powersave_anim, Brightness, pkt_set_enable_powersave_anim, Brightness,
}; };
use rog_anime::{Animations, AnimeDataBuffer, DeviceState}; use rog_anime::{Animations, AnimeDataBuffer, DeviceState};
use zbus::export::futures_util::lock::Mutex;
use zbus::object_server::SignalEmitter; use zbus::object_server::SignalEmitter;
use zbus::proxy::CacheProperties; use zbus::proxy::CacheProperties;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection}; use zbus::{interface, Connection};
use super::config::AnimeConfig; use super::config::AniMeConfig;
use super::CtrlAnime; use super::AniMe;
use crate::error::RogError; use crate::error::RogError;
use crate::Reloadable;
pub const ANIME_ZBUS_NAME: &str = "Anime";
pub const ANIME_ZBUS_PATH: &str = "/org/asuslinux";
async fn get_logind_manager<'a>() -> ManagerProxy<'a> { async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
let connection = Connection::system() let connection = Connection::system()
@@ -34,12 +31,29 @@ async fn get_logind_manager<'a>() -> ManagerProxy<'a> {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>); pub struct AniMeZbus(AniMe);
/// The struct with the main dbus methods requires this trait impl AniMeZbus {
impl crate::ZbusRun for CtrlAnimeZbus { pub fn new(anime: AniMe) -> Self {
async fn add_to_server(self, server: &mut Connection) { Self(anime)
Self::add_to_server_helper(self, ANIME_ZBUS_PATH, server).await; }
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
} }
} }
@@ -47,20 +61,14 @@ impl crate::ZbusRun for CtrlAnimeZbus {
// If the try_lock *does* succeed then any other thread trying to lock will not // If the try_lock *does* succeed then any other thread trying to lock will not
// grab it until we finish. // grab it until we finish.
#[interface(name = "org.asuslinux.Anime")] #[interface(name = "org.asuslinux.Anime")]
impl CtrlAnimeZbus { impl AniMeZbus {
/// Writes a data stream of length. Will force system thread to exit until /// Writes a data stream of length. Will force system thread to exit until
/// it is restarted /// it is restarted
async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
self.0 let bright = self.0.config.lock().await.display_brightness;
.lock() self.0.set_builtins_enabled(false, bright).await?;
.await self.0.thread_exit.store(true, Ordering::SeqCst);
.thread_exit self.0.write_data_buffer(input).await.map_err(|err| {
.store(true, Ordering::SeqCst);
self.0
.lock()
.await
.write_data_buffer(input)
.map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
err err
})?; })?;
@@ -70,68 +78,67 @@ impl CtrlAnimeZbus {
/// Set base brightness level /// Set base brightness level
#[zbus(property)] #[zbus(property)]
async fn brightness(&self) -> Brightness { async fn brightness(&self) -> Brightness {
self.0.lock().await.config.display_brightness if let Ok(config) = self.0.config.try_lock() {
return config.display_brightness;
}
Brightness::Off
} }
/// Set base brightness level /// Set base brightness level
#[zbus(property)] #[zbus(property)]
async fn set_brightness(&self, brightness: Brightness) { async fn set_brightness(&self, brightness: Brightness) {
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness)) .write_bytes(&pkt_set_brightness(brightness))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(brightness != Brightness::Off)) .write_bytes(&pkt_set_enable_display(brightness != Brightness::Off))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_brightness {}", err); warn!("ctrl_anime::set_brightness {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = brightness != Brightness::Off; let mut config = self.0.config.lock().await;
self.0.lock().await.config.display_brightness = brightness; config.display_enabled = brightness != Brightness::Off;
self.0.lock().await.config.write(); config.display_brightness = brightness;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn builtins_enabled(&self) -> bool { async fn builtins_enabled(&self) -> bool {
let lock = self.0.lock().await; if let Ok(config) = self.0.config.try_lock() {
lock.config.builtin_anims_enabled return config.builtin_anims_enabled;
}
false
} }
/// Enable the builtin animations or not. This is quivalent to "Powersave /// Enable the builtin animations or not. This is quivalent to "Powersave
/// animations" in Armory crate /// animations" in Armory crate
#[zbus(property)] #[zbus(property)]
async fn set_builtins_enabled(&self, enabled: bool) { async fn set_builtins_enabled(&self, enabled: bool) {
let brightness = self.0.lock().await.config.display_brightness; let mut config = self.0.config.lock().await;
let brightness = config.display_brightness;
self.0 self.0
.lock()
.await
.node
.set_builtins_enabled(enabled, brightness) .set_builtins_enabled(enabled, brightness)
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) })
.ok(); .ok();
if !enabled { if !enabled {
let anime_type = self.0.lock().await.anime_type; let anime_type = config.anime_type;
let data = vec![255u8; anime_type.data_length()]; let data = vec![255u8; anime_type.data_length()];
if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| { if let Ok(tmp) = AnimeDataBuffer::from_vec(anime_type, data).map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) { }) {
self.0 self.0
.lock()
.await
.node
.write_bytes(tmp.data()) .write_bytes(tmp.data())
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::set_builtins_enabled {}", err); warn!("ctrl_anime::set_builtins_enabled {}", err);
}) })
@@ -139,77 +146,78 @@ impl CtrlAnimeZbus {
} }
} }
self.0.lock().await.config.builtin_anims_enabled = enabled; config.builtin_anims_enabled = enabled;
self.0.lock().await.config.write(); config.write();
if enabled { if enabled {
self.0 self.0.thread_exit.store(true, Ordering::Release);
.lock()
.await
.thread_exit
.store(true, Ordering::Release);
} }
} }
#[zbus(property)] #[zbus(property)]
async fn builtin_animations(&self) -> Animations { async fn builtin_animations(&self) -> Animations {
self.0.lock().await.config.builtin_anims if let Ok(config) = self.0.config.try_lock() {
return config.builtin_anims;
}
Animations::default()
} }
/// Set which builtin animation is used for each stage /// Set which builtin animation is used for each stage
#[zbus(property)] #[zbus(property)]
async fn set_builtin_animations(&self, settings: Animations) { async fn set_builtin_animations(&self, settings: Animations) {
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_builtin_animations( .write_bytes(&pkt_set_builtin_animations(
settings.boot, settings.boot,
settings.awake, settings.awake,
settings.sleep, settings.sleep,
settings.shutdown, settings.shutdown,
)) ))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(true)) .write_bytes(&pkt_set_enable_powersave_anim(true))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = true; let mut config = self.0.config.lock().await;
self.0.lock().await.config.builtin_anims = settings; config.display_enabled = true;
self.0.lock().await.config.write(); config.builtin_anims = settings;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn enable_display(&self) -> bool { async fn enable_display(&self) -> bool {
self.0.lock().await.config.display_enabled if let Ok(config) = self.0.config.try_lock() {
return config.display_enabled;
}
false
} }
/// Set whether the AniMe is enabled at all /// Set whether the AniMe is enabled at all
#[zbus(property)] #[zbus(property)]
async fn set_enable_display(&self, enabled: bool) { async fn set_enable_display(&self, enabled: bool) {
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(enabled)) .write_bytes(&pkt_set_enable_display(enabled))
.await
.map_err(|err| { .map_err(|err| {
warn!("ctrl_anime::run_animation:callback {}", err); warn!("ctrl_anime::run_animation:callback {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.display_enabled = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.display_enabled = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_unplugged(&self) -> bool { async fn off_when_unplugged(&self) -> bool {
self.0.lock().await.config.off_when_unplugged if let Ok(config) = self.0.config.try_lock() {
return config.off_when_unplugged;
}
false
} }
/// Set if to turn the AniMe Matrix off when external power is unplugged /// Set if to turn the AniMe Matrix off when external power is unplugged
@@ -219,34 +227,40 @@ impl CtrlAnimeZbus {
let pow = manager.on_external_power().await.unwrap_or_default(); let pow = manager.on_external_power().await.unwrap_or_default();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!pow && !enabled)) .write_bytes(&pkt_set_enable_display(!pow && !enabled))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.off_when_unplugged = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_unplugged = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_suspended(&self) -> bool { async fn off_when_suspended(&self) -> bool {
self.0.lock().await.config.off_when_suspended if let Ok(config) = self.0.config.try_lock() {
return config.off_when_suspended;
}
false
} }
/// Set if to turn the AniMe Matrix off when the laptop is suspended /// Set if to turn the AniMe Matrix off when the laptop is suspended
#[zbus(property)] #[zbus(property)]
async fn set_off_when_suspended(&self, enabled: bool) { async fn set_off_when_suspended(&self, enabled: bool) {
self.0.lock().await.config.off_when_suspended = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_suspended = enabled;
config.write();
} }
#[zbus(property)] #[zbus(property)]
async fn off_when_lid_closed(&self) -> bool { async fn off_when_lid_closed(&self) -> bool {
self.0.lock().await.config.off_when_lid_closed if let Ok(config) = self.0.config.try_lock() {
return config.off_when_lid_closed;
}
false
} }
/// Set if to turn the AniMe Matrix off when the lid is closed /// Set if to turn the AniMe Matrix off when the lid is closed
@@ -256,47 +270,37 @@ impl CtrlAnimeZbus {
let lid = manager.lid_closed().await.unwrap_or_default(); let lid = manager.lid_closed().await.unwrap_or_default();
self.0 self.0
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(lid && !enabled)) .write_bytes(&pkt_set_enable_display(lid && !enabled))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
.ok(); .ok();
self.0.lock().await.config.off_when_lid_closed = enabled; let mut config = self.0.config.lock().await;
self.0.lock().await.config.write(); config.off_when_lid_closed = enabled;
config.write();
} }
/// The main loop is the base system set action if the user isn't running /// The main loop is the base system set action if the user isn't running
/// the user daemon /// the user daemon
async fn run_main_loop(&self, start: bool) { async fn run_main_loop(&self, start: bool) {
if start { if start {
self.0 self.0.thread_exit.store(true, Ordering::SeqCst);
.lock() self.0.run_thread(self.0.cache.system.clone(), false).await;
.await
.thread_exit
.store(true, Ordering::SeqCst);
CtrlAnime::run_thread(
self.0.clone(),
self.0.lock().await.cache.system.clone(),
false,
)
.await;
} }
} }
/// Get the device state as stored by asusd /// Get the device state as stored by asusd
// #[zbus(property)] // #[zbus(property)]
async fn device_state(&self) -> DeviceState { async fn device_state(&self) -> DeviceState {
DeviceState::from(&self.0.lock().await.config) DeviceState::from(&*self.0.config.lock().await)
} }
} }
impl crate::CtrlTask for CtrlAnimeZbus { impl crate::CtrlTask for AniMeZbus {
fn zbus_path() -> &'static str { fn zbus_path() -> &'static str {
ANIME_ZBUS_PATH "ANIME_ZBUS_PATH"
} }
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> { async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
@@ -309,21 +313,15 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// on_sleep // on_sleep
let inner = inner1.clone(); let inner = inner1.clone();
async move { async move {
let config = inner.lock().await.config.clone(); let config = inner.config.lock().await.clone();
if config.display_enabled { if config.display_enabled {
inner inner.thread_exit.store(true, Ordering::Release); // ensure clean slate
.lock()
.await
.thread_exit
.store(true, Ordering::Release); // ensure clean slate
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display( .write_bytes(&pkt_set_enable_display(
!(sleeping && config.off_when_suspended), !(sleeping && config.off_when_suspended),
)) ))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
@@ -331,12 +329,10 @@ impl crate::CtrlTask for CtrlAnimeZbus {
if config.builtin_anims_enabled { if config.builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim( .write_bytes(&pkt_set_enable_powersave_anim(
!(sleeping && config.off_when_suspended), !(sleeping && config.off_when_suspended),
)) ))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
@@ -344,18 +340,11 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} else if !sleeping && !config.builtin_anims_enabled { } else if !sleeping && !config.builtin_anims_enabled {
// Run custom wake animation // Run custom wake animation
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(false)) .write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); // ensure builtins are disabled .ok(); // ensure builtins are disabled
CtrlAnime::run_thread( inner.run_thread(inner.cache.wake.clone(), true).await;
inner.clone(),
inner.lock().await.cache.wake.clone(),
true,
)
.await;
} }
} }
} }
@@ -364,26 +353,16 @@ impl crate::CtrlTask for CtrlAnimeZbus {
// on_shutdown // on_shutdown
let inner = inner2.clone(); let inner = inner2.clone();
async move { async move {
let AnimeConfig { let AniMeConfig {
display_enabled, display_enabled,
builtin_anims_enabled, builtin_anims_enabled,
.. ..
} = inner.lock().await.config; } = *inner.config.lock().await;
if display_enabled && !builtin_anims_enabled { if display_enabled && !builtin_anims_enabled {
if shutting_down { if shutting_down {
CtrlAnime::run_thread( inner.run_thread(inner.cache.shutdown.clone(), true).await;
inner.clone(),
inner.lock().await.cache.shutdown.clone(),
true,
)
.await;
} else { } else {
CtrlAnime::run_thread( inner.run_thread(inner.cache.boot.clone(), true).await;
inner.clone(),
inner.lock().await.cache.boot.clone(),
true,
)
.await;
} }
} }
} }
@@ -392,28 +371,24 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner = inner3.clone(); let inner = inner3.clone();
// on lid change // on lid change
async move { async move {
let AnimeConfig { let AniMeConfig {
off_when_lid_closed, off_when_lid_closed,
builtin_anims_enabled, builtin_anims_enabled,
.. ..
} = inner.lock().await.config; } = *inner.config.lock().await;
if off_when_lid_closed { if off_when_lid_closed {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(!lid_closed)) .write_bytes(&pkt_set_enable_powersave_anim(!lid_closed))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
.ok(); .ok();
} }
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(!lid_closed)) .write_bytes(&pkt_set_enable_display(!lid_closed))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_lid_closed {}", err); warn!("create_sys_event_tasks::off_when_lid_closed {}", err);
}) })
@@ -425,39 +400,33 @@ impl crate::CtrlTask for CtrlAnimeZbus {
let inner = inner4.clone(); let inner = inner4.clone();
// on power change // on power change
async move { async move {
let AnimeConfig { let AniMeConfig {
off_when_unplugged, off_when_unplugged,
builtin_anims_enabled, builtin_anims_enabled,
brightness_on_battery, brightness_on_battery,
.. ..
} = inner.lock().await.config; } = *inner.config.lock().await;
if off_when_unplugged { if off_when_unplugged {
if builtin_anims_enabled { if builtin_anims_enabled {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_powersave_anim(power_plugged)) .write_bytes(&pkt_set_enable_powersave_anim(power_plugged))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_suspended {}", err); warn!("create_sys_event_tasks::off_when_suspended {}", err);
}) })
.ok(); .ok();
} }
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_enable_display(power_plugged)) .write_bytes(&pkt_set_enable_display(power_plugged))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err); warn!("create_sys_event_tasks::off_when_unplugged {}", err);
}) })
.ok(); .ok();
} else { } else {
inner inner
.lock()
.await
.node
.write_bytes(&pkt_set_brightness(brightness_on_battery)) .write_bytes(&pkt_set_brightness(brightness_on_battery))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::off_when_unplugged {}", err); warn!("create_sys_event_tasks::off_when_unplugged {}", err);
}) })
@@ -472,51 +441,54 @@ impl crate::CtrlTask for CtrlAnimeZbus {
} }
} }
impl crate::Reloadable for CtrlAnimeZbus { impl crate::Reloadable for AniMeZbus {
async fn reload(&mut self) -> Result<(), RogError> { async fn reload(&mut self) -> Result<(), RogError> {
if let Some(lock) = self.0.try_lock() { if let Ok(config) = self.0.config.try_lock() {
let anim = &lock.config.builtin_anims; let anim = &config.builtin_anims;
// Set builtins // Set builtins
if lock.config.builtin_anims_enabled { if config.builtin_anims_enabled {
lock.node.write_bytes(&pkt_set_builtin_animations( self.0
.write_bytes(&pkt_set_builtin_animations(
anim.boot, anim.boot,
anim.awake, anim.awake,
anim.sleep, anim.sleep,
anim.shutdown, anim.shutdown,
))?; ))
.await?;
} }
// Builtins enabled or na? // Builtins enabled or na?
lock.node.set_builtins_enabled( self.0
lock.config.builtin_anims_enabled, .set_builtins_enabled(config.builtin_anims_enabled, config.display_brightness)
lock.config.display_brightness, .await?;
)?;
let manager = get_logind_manager().await; let manager = get_logind_manager().await;
let lid_closed = manager.lid_closed().await.unwrap_or_default(); let lid_closed = manager.lid_closed().await.unwrap_or_default();
let power_plugged = manager.on_external_power().await.unwrap_or_default(); let power_plugged = manager.on_external_power().await.unwrap_or_default();
let turn_off = (lid_closed && lock.config.off_when_lid_closed) let turn_off = (lid_closed && config.off_when_lid_closed)
|| (!power_plugged && lock.config.off_when_unplugged); || (!power_plugged && config.off_when_unplugged);
lock.node self.0
.write_bytes(&pkt_set_enable_display(!turn_off)) .write_bytes(&pkt_set_enable_display(!turn_off))
.await
.map_err(|err| { .map_err(|err| {
warn!("create_sys_event_tasks::reload {}", err); warn!("create_sys_event_tasks::reload {}", err);
}) })
.ok(); .ok();
if turn_off || !lock.config.display_enabled { if turn_off || !config.display_enabled {
lock.node.write_bytes(&pkt_set_enable_display(false))?; self.0.write_bytes(&pkt_set_enable_display(false)).await?;
// early return so we don't run animation thread // early return so we don't run animation thread
return Ok(()); return Ok(());
} }
if !lock.config.builtin_anims_enabled && !lock.cache.boot.is_empty() { if !config.builtin_anims_enabled && !self.0.cache.boot.is_empty() {
lock.node self.0
.write_bytes(&pkt_set_enable_powersave_anim(false)) .write_bytes(&pkt_set_enable_powersave_anim(false))
.await
.ok(); .ok();
let action = lock.cache.boot.clone(); let action = self.0.cache.boot.clone();
CtrlAnime::run_thread(self.0.clone(), action, true).await; self.0.run_thread(action, true).await;
} }
} }
Ok(()) Ok(())

View File

@@ -14,6 +14,10 @@ use crate::error::RogError;
#[derive(Deserialize, Serialize, Default, Debug, Clone)] #[derive(Deserialize, Serialize, Default, Debug, Clone)]
// #[serde(default)] // #[serde(default)]
pub struct AuraConfig { pub struct AuraConfig {
#[serde(skip)]
pub led_type: AuraDeviceType,
#[serde(skip)]
pub support_data: LedSupportData,
pub config_name: String, pub config_name: String,
#[serde(skip_serializing_if = "Option::is_none", default)] #[serde(skip_serializing_if = "Option::is_none", default)]
pub ally_fix: Option<bool>, pub ally_fix: Option<bool>,
@@ -24,6 +28,8 @@ pub struct AuraConfig {
pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>, pub multizone: Option<BTreeMap<AuraModeNum, Vec<AuraEffect>>>,
pub multizone_on: bool, pub multizone_on: bool,
pub enabled: LaptopAuraPower, pub enabled: LaptopAuraPower,
#[serde(skip)]
pub per_key_mode_active: bool,
} }
impl StdConfig for AuraConfig { impl StdConfig for AuraConfig {
@@ -58,6 +64,8 @@ impl AuraConfig {
let support_data = LedSupportData::get_data(prod_id); let support_data = LedSupportData::get_data(prod_id);
let enabled = LaptopAuraPower::new(device_type, &support_data); let enabled = LaptopAuraPower::new(device_type, &support_data);
let mut config = AuraConfig { let mut config = AuraConfig {
led_type: device_type,
support_data,
config_name: format!("aura_{prod_id}.ron"), config_name: format!("aura_{prod_id}.ron"),
ally_fix: None, ally_fix: None,
brightness: LedBrightness::Med, brightness: LedBrightness::Med,
@@ -66,17 +74,18 @@ impl AuraConfig {
multizone: None, multizone: None,
multizone_on: false, multizone_on: false,
enabled, enabled,
per_key_mode_active: false,
}; };
for n in &support_data.basic_modes { for n in &config.support_data.basic_modes {
debug!("creating default for {n}"); debug!("creating default for {n}");
config config
.builtins .builtins
.insert(*n, AuraEffect::default_with_mode(*n)); .insert(*n, AuraEffect::default_with_mode(*n));
if !support_data.basic_zones.is_empty() { if !config.support_data.basic_zones.is_empty() {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in support_data.basic_zones.iter().enumerate() { for (i, tmp) in config.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: *n, mode: *n,
zone: *tmp, zone: *tmp,
@@ -138,12 +147,9 @@ impl AuraConfig {
/// Create a default for the `current_mode` if multizone and no config /// Create a default for the `current_mode` if multizone and no config
/// exists. /// exists.
pub(super) fn create_multizone_default( pub fn create_multizone_default(&mut self) -> Result<(), RogError> {
&mut self,
supported_data: &LedSupportData,
) -> Result<(), RogError> {
let mut default = vec![]; let mut default = vec![];
for (i, tmp) in supported_data.basic_zones.iter().enumerate() { for (i, tmp) in self.support_data.basic_zones.iter().enumerate() {
default.push(AuraEffect { default.push(AuraEffect {
mode: self.current_mode, mode: self.current_mode,
zone: *tmp, zone: *tmp,
@@ -183,6 +189,9 @@ impl AuraConfig {
} }
// Then replace just incase the initialised data contains new modes added // Then replace just incase the initialised data contains new modes added
config_loaded.builtins = config_init.builtins; config_loaded.builtins = config_init.builtins;
config_loaded.support_data = config_init.support_data;
config_loaded.led_type = config_init.led_type;
config_loaded.ally_fix = config_init.ally_fix;
for enabled_init in &mut config_init.enabled.states { for enabled_init in &mut config_init.enabled.states {
for enabled in &mut config_loaded.enabled.states { for enabled in &mut config_loaded.enabled.states {

View File

@@ -0,0 +1,219 @@
use std::sync::Arc;
use config::AuraConfig;
use config_traits::StdConfig;
use log::info;
use rog_aura::keyboard::{AuraLaptopUsbPackets, LedUsbPackets};
use rog_aura::usb::{AURA_LAPTOP_LED_APPLY, AURA_LAPTOP_LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, AURA_LAPTOP_LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Aura {
pub hid: Option<Arc<Mutex<HidRaw>>>,
pub backlight: Option<Arc<Mutex<KeyboardBacklight>>>,
pub config: Arc<Mutex<AuraConfig>>,
}
impl Aura {
/// Initialise the device if required.
pub async fn do_initialization(&self) -> Result<(), RogError> {
Ok(())
}
pub async fn lock_config(&self) -> MutexGuard<AuraConfig> {
self.config.lock().await
}
/// Will lock the internal config and update. If anything else has locked
/// this in scope then a deadlock can occur.
pub async fn update_config(&self) -> Result<(), RogError> {
let mut config = self.config.lock().await;
let bright = if let Some(bl) = self.backlight.as_ref() {
bl.lock().await.get_brightness().unwrap_or_default()
} else {
config.brightness.into()
};
config.read();
config.brightness = bright.into();
config.write();
Ok(())
}
pub async fn write_current_config_mode(&self, config: &mut AuraConfig) -> Result<(), RogError> {
if config.multizone_on {
let mode = config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if config.multizone.is_none() {
create = true;
} else if let Some(multizones) = config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
config.create_multizone_default()?;
}
if let Some(multizones) = config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_effect_and_apply(config.led_type, &mode).await?;
}
}
}
} else {
let mode = config.current_mode;
if let Some(effect) = config.builtins.get(&mode).cloned() {
self.write_effect_and_apply(config.led_type, &effect)
.await?;
}
}
Ok(())
}
/// Write the AuraEffect to the device. Will lock `backlight` or `hid`.
///
/// If per-key or software-mode is active it must be marked as disabled in
/// config.
pub async fn write_effect_and_apply(
&self,
dev_type: AuraDeviceType,
mode: &AuraEffect,
) -> Result<(), RogError> {
if matches!(dev_type, AuraDeviceType::LaptopKeyboardTuf) {
if let Some(platform) = &self.backlight {
let buf = [
1,
mode.mode as u8,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.lock().await.set_kbd_rgb_mode(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let bytes: [u8; AURA_LAPTOP_LED_MSG_LEN] = mode.into();
let hid_raw = hid_raw.lock().await;
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&AURA_LAPTOP_LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
Ok(())
}
pub async fn set_brightness(&self, value: u8) -> Result<(), RogError> {
if let Some(backlight) = &self.backlight {
backlight.lock().await.set_brightness(value)?;
return Ok(());
}
Err(RogError::MissingFunction(
"No LED backlight control available".to_string(),
))
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub async fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(backlight) = &self.backlight {
// TODO: tuf bool array
let buf = config.enabled.to_bytes(config.led_type);
backlight.lock().await.set_kbd_rgb_state(&buf)?;
}
} else if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if let Some(p) = config.enabled.states.first() {
if p.zone == PowerZones::Ally {
let msg = [0x5d, 0xd1, 0x09, 0x01, p.new_to_byte() as u8, 0x0, 0x0];
hid_raw.write_bytes(&msg)?;
return Ok(());
}
}
let bytes = config.enabled.to_bytes(config.led_type);
let msg = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&msg)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub async fn write_effect_block(
&self,
config: &mut AuraConfig,
effect: &AuraLaptopUsbPackets,
) -> Result<(), RogError> {
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if let Some(hid_raw) = &self.hid {
let hid_raw = hid_raw.lock().await;
if pkt_type != PER_KEY_TYPE {
config.per_key_mode_active = false;
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&AURA_LAPTOP_LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
} else {
if !config.per_key_mode_active {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
config.per_key_mode_active = true;
}
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
}
} else if matches!(config.led_type, rog_aura::AuraDeviceType::LaptopKeyboardTuf) {
if let Some(tuf) = &self.backlight {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.lock().await.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
}
Ok(())
}
pub async fn fix_ally_power(&mut self) -> Result<(), RogError> {
if self.config.lock().await.led_type == AuraDeviceType::Ally {
if let Some(hid_raw) = &self.hid {
let mut config = self.config.lock().await;
if config.ally_fix.is_none() {
let msg = [0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff];
hid_raw.lock().await.write_bytes(&msg)?;
info!("Reset Ally power settings to base");
config.ally_fix = Some(true);
}
config.write();
}
}
Ok(())
}
}

View File

@@ -0,0 +1,343 @@
use std::collections::BTreeMap;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::fdo::Error as ZbErr;
use zbus::object_server::SignalEmitter;
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Aura;
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct AuraZbus(Aura);
impl AuraZbus {
pub fn new(aura: Aura) -> Self {
Self(aura)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
// _signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "org.asuslinux.Aura")]
impl AuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
self.0.config.lock().await.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.get_brightness().map(|n| n.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
if let Some(bl) = self.0.backlight.as_ref() {
return Ok(bl.lock().await.set_brightness(brightness.into())?);
}
Err(ZbErr::Failed("No sysfs brightness control".to_string()))
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let config = self.0.config.lock().await;
Ok(config.support_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await;
// Ok(config.current_mode)
if let Ok(config) = self.0.config.try_lock() {
Ok(config.current_mode)
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
config.current_mode = num;
self.0.write_current_config_mode(&mut config).await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.write();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Ok(config) = self.0.config.try_lock() {
let mode = config.current_mode;
match config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
if !config.support_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !config.support_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
self.0
.write_effect_and_apply(config.led_type, &effect)
.await?;
if config.brightness == LedBrightness::Off {
config.brightness = LedBrightness::Med;
}
self.0.set_brightness(config.brightness.into()).await?;
config.set_builtin(effect);
config.write();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let config = self.0.config.lock().await;
config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let config = self.0.config.lock().await;
config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
config.write();
Ok(self.0.set_power_states(&config).await.map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> Result<(), ZbErr> {
let mut config = self.0.config.lock().await;
self.0.write_effect_block(&mut config, &data).await?;
Ok(())
}
}
impl CtrlTask for AuraZbus {
fn zbus_path() -> &'static str {
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
// unwrap as we want to bomb out of the task
async move {
if !sleeping {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner1.backlight {
backlight
.lock()
.await
.set_brightness(inner1.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
let mut config = inner1.config.lock().await;
inner1
.write_current_config_mode(&mut config)
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
} else if sleeping {
inner1
.update_config()
.await
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
info!("CtrlKbdLedTask reloading brightness and modes");
if let Some(backlight) = &inner3.backlight {
// unwrap as we want to bomb out of the task
backlight
.lock()
.await
.set_brightness(inner3.config.lock().await.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})
.unwrap();
}
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
// let ctrl2 = self.0.clone();
// let ctrl = self.0.lock().await;
// if ctrl.led_node.has_brightness_control() {
// let watch = ctrl.led_node.monitor_brightness()?;
// tokio::spawn(async move {
// let mut buffer = [0; 32];
// watch
// .into_event_stream(&mut buffer)
// .unwrap()
// .for_each(|_| async {
// if let Some(lock) = ctrl2.try_lock() {
// load_save(true, lock).unwrap(); // unwrap as we want
// // to
// // bomb out of the
// // task
// }
// })
// .await;
// });
// }
Ok(())
}
}
impl Reloadable for AuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
self.0.fix_ally_power().await?;
debug!("reloading keyboard mode");
let mut config = self.0.lock_config().await;
self.0.write_current_config_mode(&mut config).await?;
debug!("reloading power states");
self.0
.set_power_states(&config)
.await
.map_err(|err| warn!("{err}"))
.ok();
Ok(())
}
}

368
asusd/src/aura_manager.rs Normal file
View File

@@ -0,0 +1,368 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::sync::Arc;
use futures_lite::future::block_on;
use log::{debug, error, info, warn};
use mio::{Events, Interest, Poll, Token};
use rog_platform::error::PlatformError;
use rog_platform::hid_raw::HidRaw;
use tokio::sync::Mutex;
use udev::{Device, MonitorBuilder};
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::aura_anime::trait_impls::AniMeZbus;
use crate::aura_laptop::trait_impls::AuraZbus;
use crate::aura_slash::trait_impls::SlashZbus;
use crate::aura_types::DeviceHandle;
use crate::error::RogError;
pub const ASUS_ZBUS_PATH: &str = "/org/asuslinux";
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
if path.contains('.') {
warn!("dbus path for {id_product} contains `.`, removing");
path.replace('.', "").clone_into(&mut path);
}
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}
fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/tuf")).into()
}
fn dbus_path_for_slash() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/slash")).into()
}
fn dbus_path_for_anime() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{ASUS_ZBUS_PATH}/anime")).into()
}
// TODO:
// - make this the HID manager (and universal)
// - *really* need to make most of this actual kernel drivers
// - LED class
// - RGB modes (how, attribute?)
// - power features (how, attribute?)
// - what about per-key stuff?
// - how would the AniMe be exposed? Just a series of LEDs?
/// A device.
///
/// Each controller within should track its dbus path so it can be removed if
/// required.
#[derive(Debug)]
pub struct AsusDevice {
device: DeviceHandle,
dbus_path: OwnedObjectPath,
}
pub struct DeviceManager {
_dbus_connection: Connection,
}
impl DeviceManager {
async fn init_hid_devices(
connection: &Connection,
device: Device,
) -> Result<Vec<AsusDevice>, RogError> {
let mut devices = Vec::new();
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
if let Some(vendor_id) = usb_device.attribute_value("idVendor") {
if vendor_id != "0b05" {
debug!("Not ASUS vendor ID");
return Ok(devices);
}
// Almost all devices are identified by the productId.
// So let's see what we have and:
// 1. Generate an interface path
// 2. Create the device
// Use the top-level endpoint, not the parent
if let Ok(hidraw) = HidRaw::from_device(device) {
debug!("Testing device {usb_id:?}");
let dev = Arc::new(Mutex::new(hidraw));
// SLASH DEVICE
if let Ok(dev_type) = DeviceHandle::new_slash_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_slash());
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
// ANIME MATRIX DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_anime_hid(
dev.clone(),
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_anime());
let ctrl = AniMeZbus::new(anime);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
// AURA LAPTOP DEVICE
if let Ok(dev_type) = DeviceHandle::maybe_laptop_aura(
dev,
usb_id.to_str().unwrap_or_default(),
)
.await
{
if let DeviceHandle::Aura(aura) = dev_type.clone() {
let path =
dbus_path_for_dev(&usb_device).unwrap_or(dbus_path_for_tuf());
let ctrl = AuraZbus::new(aura);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
}
}
}
}
}
Ok(devices)
}
/// To be called on daemon startup
async fn init_all_hid(connection: &Connection) -> Result<Vec<AsusDevice>, RogError> {
// track and ensure we use only one hidraw per prod_id
// let mut interfaces = HashSet::new();
let mut devices: Vec<AsusDevice> = Vec::new();
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::IoPath("enumerator".to_owned(), e))?
{
devices.append(&mut Self::init_hid_devices(connection, device).await?);
}
// debug!("Found devices: {devices:?}");
Ok(devices)
}
pub async fn find_all_devices(connection: &Connection) -> Vec<AsusDevice> {
let mut devices: Vec<AsusDevice> = Vec::new();
// HID first, always
if let Ok(devs) = &mut Self::init_all_hid(connection).await {
devices.append(devs);
}
// USB after, need to check if HID picked something up and if so, skip it
let mut do_anime = true;
let mut do_slash = true;
for dev in devices.iter() {
if matches!(dev.device, DeviceHandle::Slash(_)) {
do_slash = false;
}
if matches!(dev.device, DeviceHandle::AniMe(_)) {
do_anime = false;
}
}
if do_slash {
if let Ok(dev_type) = DeviceHandle::new_slash_usb().await {
if let DeviceHandle::Slash(slash) = dev_type.clone() {
let path = dbus_path_for_slash();
let ctrl = SlashZbus::new(slash);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
} else {
info!("Tested device was not Slash");
}
}
if do_anime {
if let Ok(dev_type) = DeviceHandle::maybe_anime_usb().await {
// TODO: this is copy/pasted
if let DeviceHandle::AniMe(anime) = dev_type.clone() {
let path = dbus_path_for_anime();
let ctrl = AniMeZbus::new(anime);
ctrl.start_tasks(connection, path.clone()).await.unwrap();
devices.push(AsusDevice {
device: dev_type,
dbus_path: path,
});
}
} else {
info!("Tested device was not AniMe Matrix");
}
}
devices
}
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let devices = Arc::new(Mutex::new(Self::find_all_devices(&conn_copy).await));
let manager = Self {
_dbus_connection: connection,
};
// TODO: The /sysfs/ LEDs don't cause events, so they need to be manually
// checked for and added
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
let rt = tokio::runtime::Runtime::new().expect("Unable to create Runtime");
let _enter = rt.enter();
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let action = event.action().unwrap_or_default();
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
let devices = devices.clone();
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
// Find the indexs of devices matching the path
let removals: Vec<usize> = devices
.lock()
.await
.iter()
.enumerate()
.filter_map(|(i, dev)| {
if dev.dbus_path == path {
Some(i)
} else {
None
}
})
.collect();
if removals.is_empty() {
return Ok(());
}
info!("removing: {path:?}");
// Iter in reverse so as to not screw up indexing
for index in removals.iter().rev() {
let dev = devices.lock().await.remove(*index);
let path = path.clone();
let res = match dev.device {
DeviceHandle::Aura(_) => {
conn_copy
.object_server()
.remove::<AuraZbus, _>(&path)
.await?
}
DeviceHandle::Slash(_) => {
conn_copy
.object_server()
.remove::<SlashZbus, _>(&path)
.await?
}
DeviceHandle::AniMe(_) => {
conn_copy
.object_server()
.remove::<AniMeZbus, _>(&path)
.await?
}
DeviceHandle::Ally(_) => todo!(),
DeviceHandle::OldAura(_) => todo!(),
DeviceHandle::TufLedClass(_) => todo!(),
DeviceHandle::MulticolourLed => todo!(),
DeviceHandle::None => todo!(),
};
info!("AuraManager removed: {path:?}, {res}");
}
Ok::<(), RogError>(())
});
}
} else if action == "add" {
let evdev = event.device();
let conn_copy = conn_copy.clone();
block_on(async move {
if let Ok(mut new_devs) = Self::init_hid_devices(&conn_copy, evdev)
.await
.map_err(|e| error!("Couldn't add new device: {e:?}"))
{
devices.lock().await.append(&mut new_devs);
}
});
};
}
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}

View File

@@ -1,5 +1,5 @@
use config_traits::{StdConfig, StdConfigLoad}; use config_traits::{StdConfig, StdConfigLoad};
use rog_slash::{DeviceState, SlashMode}; use rog_slash::{DeviceState, SlashMode, SlashType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
const CONFIG_FILE: &str = "slash.ron"; const CONFIG_FILE: &str = "slash.ron";
@@ -7,6 +7,8 @@ const CONFIG_FILE: &str = "slash.ron";
/// Config for base system actions for the anime display /// Config for base system actions for the anime display
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct SlashConfig { pub struct SlashConfig {
#[serde(skip)]
pub slash_type: SlashType,
pub slash_enabled: bool, pub slash_enabled: bool,
pub slash_brightness: u8, pub slash_brightness: u8,
pub slash_interval: u8, pub slash_interval: u8,
@@ -20,6 +22,7 @@ impl Default for SlashConfig {
slash_brightness: 255, slash_brightness: 255,
slash_interval: 0, slash_interval: 0,
slash_mode: SlashMode::Bounce, slash_mode: SlashMode::Bounce,
slash_type: SlashType::Unsupported,
} }
} }
} }

View File

@@ -0,0 +1,70 @@
use std::sync::Arc;
use config::SlashConfig;
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::SlashType;
use tokio::sync::{Mutex, MutexGuard};
use crate::error::RogError;
pub mod config;
pub mod trait_impls;
#[derive(Debug, Clone)]
pub struct Slash {
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>,
}
impl Slash {
pub fn new(
hid: Option<Arc<Mutex<HidRaw>>>,
usb: Option<Arc<Mutex<USBRaw>>>,
config: Arc<Mutex<SlashConfig>>,
) -> Self {
Self { hid, usb, config }
}
pub async fn lock_config(&self) -> MutexGuard<SlashConfig> {
self.config.lock().await
}
pub async fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
if let Some(hid) = &self.hid {
hid.lock().await.write_bytes(message)?;
} else if let Some(usb) = &self.usb {
usb.lock().await.write_bytes(message)?;
}
Ok(())
}
/// Initialise the device if required. Locks the internal config so be wary
/// of deadlocks.
pub async fn do_initialization(&self) -> Result<(), RogError> {
// Don't try to initialise these models as the asus drivers already did
let config = self.config.lock().await;
if !matches!(config.slash_type, SlashType::GA605 | SlashType::GU605) {
for pkt in &pkts_for_init(config.slash_type) {
self.write_bytes(pkt).await?;
}
}
// Apply config upon initialization
let option_packets = pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
config.slash_interval,
);
self.write_bytes(&option_packets).await?;
let mode_packets = pkt_set_mode(config.slash_type, config.slash_mode);
// self.node.write_bytes(&mode_packets[0])?;
self.write_bytes(&mode_packets[1]).await?;
Ok(())
}
}

View File

@@ -0,0 +1,190 @@
use config_traits::StdConfig;
use log::{debug, error, warn};
use rog_slash::usb::{pkt_save, pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::zvariant::OwnedObjectPath;
use zbus::{interface, Connection};
use super::Slash;
use crate::error::RogError;
use crate::Reloadable;
#[derive(Clone)]
pub struct SlashZbus(Slash);
impl SlashZbus {
pub fn new(slash: Slash) -> Self {
Self(slash)
}
pub async fn start_tasks(
mut self,
connection: &Connection,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
self.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), self)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
Ok(())
}
}
#[interface(name = "org.asuslinux.Slash")]
impl SlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock_config().await;
lock.slash_enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut config = self.0.lock_config().await;
let brightness = if enabled && config.slash_brightness == 0 {
0x88
} else {
config.slash_brightness
};
self.0
.write_bytes(&pkt_set_options(
config.slash_type,
enabled,
brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_enabled = enabled;
config.slash_brightness = brightness;
config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut config = self.0.lock_config().await;
let enabled = brightness > 0;
self.0
.write_bytes(&pkt_set_options(
config.slash_type,
enabled,
brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_enabled = enabled;
config.slash_brightness = brightness;
config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut config = self.0.lock_config().await;
self.0
.write_bytes(&pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_interval = interval;
config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let config = self.0.lock_config().await;
config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut config = self.0.lock_config().await;
let command_packets = pkt_set_mode(config.slash_type, slash_mode);
// self.node.write_bytes(&command_packets[0])?;
self.0
.write_bytes(&command_packets[1])
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
self.0
.write_bytes(&pkt_save(config.slash_type))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
config.slash_mode = slash_mode;
config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let config = self.0.lock_config().await;
DeviceState::from(&*config)
}
}
impl Reloadable for SlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
debug!("reloading slash settings");
let config = self.0.lock_config().await;
self.0
.write_bytes(&pkt_set_options(
config.slash_type,
config.slash_enabled,
config.slash_brightness,
config.slash_interval,
))
.await
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
Ok(())
}
}

183
asusd/src/aura_types.rs Normal file
View File

@@ -0,0 +1,183 @@
use std::sync::Arc;
use config_traits::{StdConfig, StdConfigLoad};
use log::{debug, error, info};
use rog_anime::error::AnimeError;
use rog_anime::usb::get_anime_type;
use rog_anime::AnimeType;
use rog_aura::AuraDeviceType;
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::SlashType;
use tokio::sync::Mutex;
use crate::aura_anime::config::AniMeConfig;
use crate::aura_anime::AniMe;
use crate::aura_laptop::config::AuraConfig;
use crate::aura_laptop::Aura;
use crate::aura_slash::config::SlashConfig;
use crate::aura_slash::Slash;
use crate::error::RogError;
pub enum _DeviceHandle {
/// The AniMe devices require USBRaw as they are not HID devices
Usb(USBRaw),
HidRaw(HidRaw),
LedClass(KeyboardBacklight),
/// TODO
MulticolourLed,
None,
}
#[derive(Debug, Clone)]
pub enum DeviceHandle {
Aura(Aura),
Slash(Slash),
/// The AniMe devices require USBRaw as they are not HID devices
AniMe(AniMe),
Ally(Arc<Mutex<HidRaw>>),
OldAura(Arc<Mutex<HidRaw>>),
/// TUF laptops have an aditional set of attributes added to the LED /sysfs/
TufLedClass(Arc<Mutex<HidRaw>>),
/// TODO
MulticolourLed,
None,
}
impl DeviceHandle {
/// Try Slash HID. If one exists it is initialsed and returned.
pub async fn new_slash_hid(
device: Arc<Mutex<HidRaw>>,
prod_id: &str,
) -> Result<Self, RogError> {
debug!("Testing for HIDRAW Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported)
|| slash_type
.prod_id_str()
.to_lowercase()
.trim_start_matches("0x")
!= prod_id
{
log::info!("Unknown or invalid slash: {prod_id:?}, skipping");
return Err(RogError::NotFound("No slash device".to_string()));
}
info!("Found slash type {slash_type:?}: {prod_id}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(Some(device), None, Arc::new(Mutex::new(config)));
slash.do_initialization().await?;
Ok(Self::Slash(slash))
}
/// Try Slash USB. If one exists it is initialsed and returned.
pub async fn new_slash_usb() -> Result<Self, RogError> {
debug!("Testing for USB Slash");
let slash_type = SlashType::from_dmi();
if matches!(slash_type, SlashType::Unsupported) {
return Err(RogError::Slash(SlashError::NoDevice));
}
if let Ok(usb) = USBRaw::new(slash_type.prod_id()) {
info!("Found Slash USB {slash_type:?}");
let mut config = SlashConfig::new().load();
config.slash_type = slash_type;
let slash = Slash::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)),
);
slash.do_initialization().await?;
Ok(Self::Slash(slash))
} else {
Err(RogError::NotFound("No slash device found".to_string()))
}
}
/// Try AniMe Matrix HID. If one exists it is initialsed and returned.
pub async fn maybe_anime_hid(
device: Arc<Mutex<HidRaw>>,
prod_id: &str,
) -> Result<Self, RogError> {
debug!("Testing for HIDRAW AniMe");
let anime_type = AnimeType::from_dmi();
dbg!(prod_id);
if matches!(anime_type, AnimeType::Unsupported) || prod_id != "193b" {
log::info!("Unknown or invalid AniMe: {prod_id:?}, skipping");
return Err(RogError::NotFound("No anime-matrix device".to_string()));
}
info!("Found AniMe Matrix HIDRAW {anime_type:?}: {prod_id}");
let mut config = AniMeConfig::new().load();
config.anime_type = anime_type;
let mut anime = AniMe::new(Some(device), None, Arc::new(Mutex::new(config)));
anime.do_initialization().await?;
Ok(Self::AniMe(anime))
}
pub async fn maybe_anime_usb() -> Result<Self, RogError> {
debug!("Testing for USB AniMe");
let anime_type = get_anime_type();
if matches!(anime_type, AnimeType::Unsupported) {
info!("No Anime Matrix capable laptop found");
return Err(RogError::Anime(AnimeError::NoDevice));
}
if let Ok(usb) = USBRaw::new(0x193b) {
info!("Found AniMe Matrix USB {anime_type:?}");
let mut config = AniMeConfig::new().load();
config.anime_type = anime_type;
let mut anime = AniMe::new(
None,
Some(Arc::new(Mutex::new(usb))),
Arc::new(Mutex::new(config)),
);
anime.do_initialization().await?;
Ok(Self::AniMe(anime))
} else {
Err(RogError::NotFound(
"No AnimeMatrix device found".to_string(),
))
}
}
pub async fn maybe_laptop_aura(
device: Arc<Mutex<HidRaw>>,
prod_id: &str,
) -> Result<Self, RogError> {
debug!("Testing for laptop aura");
let aura_type = AuraDeviceType::from(prod_id);
if !matches!(
aura_type,
AuraDeviceType::LaptopKeyboard2021
| AuraDeviceType::LaptopKeyboardPre2021
| AuraDeviceType::LaptopKeyboardTuf
) {
log::info!("Unknown or invalid laptop aura: {prod_id:?}, skipping");
return Err(RogError::NotFound("No laptop aura device".to_string()));
}
info!("Found laptop aura type {prod_id:?}");
let backlight = KeyboardBacklight::new()
.map_err(|e| error!("Keyboard backlight error: {e:?}"))
.map_or(None, |k| {
info!("Found sysfs backlight control");
Some(Arc::new(Mutex::new(k)))
});
let mut config = AuraConfig::load_and_update_config(prod_id);
config.led_type = aura_type;
let aura = Aura {
hid: Some(device),
backlight,
config: Arc::new(Mutex::new(config)),
};
aura.do_initialization().await?;
Ok(Self::Aura(aura))
}
}

View File

@@ -1,296 +0,0 @@
pub mod config;
/// Implements `CtrlTask`, Reloadable, `ZbusRun`
pub mod trait_impls;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use ::zbus::export::futures_util::lock::Mutex;
use config_traits::{StdConfig, StdConfigLoad};
use log::{error, info, warn};
use rog_anime::error::AnimeError;
use rog_anime::usb::{
get_maybe_anime_type, pkt_flush, pkt_set_brightness, pkt_set_enable_display,
pkt_set_enable_powersave_anim, pkts_for_init, Brightness,
};
use rog_anime::{ActionData, AnimeDataBuffer, AnimePacketType, AnimeType};
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use self::config::{AnimeConfig, AnimeConfigCached};
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
pub fn set_builtins_enabled(&self, enabled: bool, bright: Brightness) -> Result<(), RogError> {
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
self.write_bytes(&pkt_set_enable_display(enabled))?;
self.write_bytes(&pkt_set_brightness(bright))?;
self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
}
}
pub struct CtrlAnime {
// node: HidRaw,
node: Node,
anime_type: AnimeType,
cache: AnimeConfigCached,
config: AnimeConfig,
// set to force thread to exit
thread_exit: Arc<AtomicBool>,
// Set to false when the thread exits
thread_running: Arc<AtomicBool>,
}
impl CtrlAnime {
#[inline]
pub fn new() -> Result<CtrlAnime, RogError> {
let anime_type = get_maybe_anime_type()?;
if matches!(anime_type, AnimeType::Unsupported) {
info!("No Anime Matrix capable laptop found");
return Err(RogError::Anime(AnimeError::NoDevice));
}
let usb = USBRaw::new(0x193b).ok();
let hid = HidRaw::new("193b").ok();
let node = if usb.is_some() {
info!("Anime using the USB interface");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Anime using the HID interface");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Anime(AnimeError::NoDevice));
};
// TODO: something better to set wakeups disabled
// if matches!(node, Node::Usb(_)) {
// if let Ok(mut enumerator) = udev::Enumerator::new() {
// enumerator.match_subsystem("usb").ok();
// enumerator.match_attribute("idProduct", "193b").ok();
// if let Ok(mut enumer) = enumerator.scan_devices() {
// if let Some(mut dev) = enumer.next() {
// dev.set_attribute_value("power/wakeup", "disabled").ok();
// }
// }
// }
// }
let mut config = AnimeConfig::new().load();
info!("Device has an AniMe Matrix display: {anime_type:?}");
let mut cache = AnimeConfigCached::default();
if let Err(e) = cache.init_from_config(&config, anime_type) {
error!("Trying to cache the Anime Config failed, will reset to default config: {e:?}");
config.rename_file_old();
config = AnimeConfig::new();
config.write();
}
let ctrl = CtrlAnime {
node,
anime_type,
cache,
config,
thread_exit: Arc::new(AtomicBool::new(false)),
thread_running: Arc::new(AtomicBool::new(false)),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
// let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
/// Start an action thread. This is classed as a singleton and there should
/// be only one running - so the thread uses atomics to signal run/exit.
///
/// Because this also writes to the usb device, other write tries (display
/// only) *must* get the mutex lock and set the `thread_exit` atomic.
async fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
if actions.is_empty() {
warn!("AniMe system actions was empty");
return;
}
if let Some(lock) = inner.try_lock() {
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(false))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for
// the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop,
// including nested
// The only reason for this outer thread is to prevent blocking while waiting
// for the next spawned thread to exit
// TODO: turn this in to async task (maybe? COuld still risk blocking main
// thread)
std::thread::Builder::new()
.name("AniMe system thread start".into())
.spawn(move || {
info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main
let thread_exit;
let thread_running;
let anime_type;
loop {
if let Some(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone();
anime_type = lock.anime_type;
break;
}
}
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
while thread_running.load(Ordering::SeqCst) {
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
}
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop {
for action in &actions {
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
match action {
ActionData::Animation(frames) => {
rog_anime::run_animation(frames, &|frame| {
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: animation sub-loop was asked to exit");
return Ok(true); // Do safe exit
}
inner
.try_lock()
.map(|lock| {
lock.write_data_buffer(frame)
.map_err(|err| {
warn!(
"rog_anime::run_animation:callback {}",
err
);
})
.ok();
false // Don't exit yet
})
.map_or_else(
|| {
warn!("rog_anime::run_animation:callback failed");
Err(AnimeError::NoFrames)
},
Ok,
)
});
if thread_exit.load(Ordering::Acquire) {
info!("rog-anime: sub-loop exited and main loop exiting now");
break 'main;
}
}
ActionData::Image(image) => {
once = false;
if let Some(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
}
}
ActionData::Pause(duration) => sleep(*duration),
ActionData::AudioEq
| ActionData::SystemInfo
| ActionData::TimeDate
| ActionData::Matrix => {}
}
}
if thread_exit.load(Ordering::SeqCst) {
break 'main;
}
if once || actions.is_empty() {
break 'main;
}
}
// Clear the display on exit
if let Some(lock) = inner.try_lock() {
if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
.map_err(|e| error!("{}", e))
{
lock.write_data_buffer(data)
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
lock.node
.write_bytes(&pkt_set_enable_powersave_anim(
lock.config.builtin_anims_enabled,
))
.map_err(|err| {
warn!("rog_anime::run_animation:callback {}", err);
})
.ok();
}
// Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst);
info!("AniMe system thread exited");
})
.map(|err| info!("AniMe system thread: {:?}", err))
.ok();
}
/// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config.
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> {
for led in buffer.data_mut().iter_mut() {
let mut bright = *led as f32;
if bright > 254.0 {
bright = 254.0;
}
*led = bright as u8;
}
let data = AnimePacketType::try_from(buffer)?;
for row in &data {
self.node.write_bytes(row)?;
}
self.node.write_bytes(&pkt_flush())?;
Ok(())
}
fn do_initialization(&self) -> Result<(), RogError> {
let pkts = pkts_for_init();
self.node.write_bytes(&pkts[0])?;
self.node.write_bytes(&pkts[1])?;
Ok(())
}
}

View File

@@ -1,526 +0,0 @@
use std::collections::HashSet;
use config_traits::StdConfig;
use dmi_id::DMIID;
use inotify::Inotify;
use log::{debug, info, warn};
use rog_aura::aura_detection::LedSupportData;
use rog_aura::keyboard::{LedUsbPackets, UsbPackets};
use rog_aura::usb::{LED_APPLY, LED_SET};
use rog_aura::{AuraDeviceType, AuraEffect, LedBrightness, PowerZones, LED_MSG_LEN};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use udev::Device;
use zbus::zvariant::OwnedObjectPath;
use zbus::Connection;
use super::config::AuraConfig;
use crate::ctrl_aura::manager::{dbus_path_for_dev, dbus_path_for_tuf, start_tasks};
use crate::ctrl_aura::trait_impls::CtrlAuraZbus;
use crate::error::RogError;
use crate::CtrlTask;
#[derive(Debug)]
pub enum LEDNode {
/// Brightness and/or TUF RGB controls
KbdLed(KeyboardBacklight),
/// Raw HID handle
Rog(Option<KeyboardBacklight>, HidRaw),
}
impl LEDNode {
// TODO: move various methods upwards to this
pub fn set_brightness(&self, value: u8) -> Result<(), RogError> {
match self {
LEDNode::KbdLed(k) => k.set_brightness(value)?,
LEDNode::Rog(k, r) => {
if let Some(k) = k {
k.set_brightness(value)?;
let x = k.get_brightness()?;
if x != value {
debug!(
"Kernel brightness control didn't read back correct value, setting \
with raw hid"
);
r.write_bytes(&[0x5a, 0xba, 0xc5, 0xc4, value])?;
}
} else {
debug!("No brightness control found, trying raw write");
r.write_bytes(&[0x5a, 0xba, 0xc5, 0xc4, value])?;
}
}
}
Ok(())
}
pub fn get_brightness(&self) -> Result<u8, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.get_brightness()?,
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.get_brightness()?
} else {
debug!("No brightness control found");
return Err(RogError::MissingFunction(
"No keyboard brightness control found".to_string(),
));
}
}
})
}
pub fn monitor_brightness(&self) -> Result<Inotify, RogError> {
Ok(match self {
LEDNode::KbdLed(k) => k.monitor_brightness()?,
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.monitor_brightness()?
} else {
debug!("No brightness control found");
return Err(RogError::MissingFunction(
"No keyboard brightness control found".to_string(),
));
}
}
})
}
pub fn has_brightness_control(&self) -> bool {
match self {
LEDNode::KbdLed(k) => k.has_brightness(),
LEDNode::Rog(k, _) => {
if let Some(k) = k {
k.has_brightness()
} else {
false
}
}
}
}
}
/// Individual controller for one Aura device
pub struct CtrlKbdLed {
pub led_type: AuraDeviceType,
pub led_node: LEDNode,
pub supported_data: LedSupportData, // TODO: is storing this really required?
pub per_key_mode_active: bool,
pub config: AuraConfig,
pub dbus_path: OwnedObjectPath,
}
impl CtrlKbdLed {
pub fn add_to_dbus_and_start(
self,
interfaces: &mut HashSet<OwnedObjectPath>,
conn: Connection,
) -> Result<(), RogError> {
let dbus_path = self.dbus_path.clone();
let dbus_path_cpy = self.dbus_path.clone();
info!(
"AuraManager starting device at: {:?}, {:?}",
dbus_path, self.led_type
);
let conn_copy = conn.clone();
let sig_ctx1 = CtrlAuraZbus::signal_context(&conn_copy)?;
let sig_ctx2 = CtrlAuraZbus::signal_context(&conn_copy)?;
let zbus = CtrlAuraZbus::new(self, sig_ctx1);
tokio::spawn(
async move { start_tasks(zbus, conn_copy.clone(), sig_ctx2, dbus_path).await },
);
interfaces.insert(dbus_path_cpy);
Ok(())
}
/// Build and init a `CtrlKbdLed` from a udev device. Maybe.
/// This will initialise the config also.
pub fn maybe_device(
device: Device,
interfaces: &mut HashSet<OwnedObjectPath>,
) -> Result<Option<Self>, RogError> {
// usb_device gives us a product and vendor ID
if let Some(usb_device) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
let dbus_path = dbus_path_for_dev(&usb_device).unwrap_or_default();
if interfaces.contains(&dbus_path) {
debug!("Already a ctrl at {dbus_path:?}, ignoring this end-point");
return Ok(None);
}
// The asus_wmi driver latches MCU that controls the USB endpoints
if let Some(parent) = device.parent() {
if let Some(driver) = parent.driver() {
// There is a tree of devices added so filter by driver
if driver != "asus" {
return Ok(None);
}
} else {
return Ok(None);
}
}
// Device is something like 002, while its parent is the MCU
// Think of it like the device is an endpoint of the USB device attached
let mut prod_id = String::new();
if let Some(usb_id) = usb_device.attribute_value("idProduct") {
prod_id = usb_id.to_string_lossy().to_string();
let aura_device = AuraDeviceType::from(prod_id.as_str());
if aura_device == AuraDeviceType::Unknown {
log::debug!("Unknown or invalid device: {usb_id:?}, skipping");
return Ok(None);
}
}
let dev_node = if let Some(dev_node) = usb_device.devnode() {
dev_node
} else {
debug!("Device has no devnode, skipping");
return Ok(None);
};
info!("AuraControl found device at: {:?}", dev_node);
let dev = HidRaw::from_device(device)?;
let mut controller = Self::from_hidraw(dev, dbus_path.clone())?;
controller.config = AuraConfig::load_and_update_config(&prod_id);
interfaces.insert(dbus_path);
return Ok(Some(controller));
}
Ok(None)
}
pub fn find_all() -> Result<Vec<Self>, RogError> {
info!("Searching for all Aura devices");
let mut devices = Vec::new();
let mut interfaces = HashSet::new(); // track and ensure we use only one hidraw per prod_id
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
})?;
for end_point in enumerator.scan_devices()? {
// maybe?
if let Some(device) = Self::maybe_device(end_point, &mut interfaces)? {
devices.push(device);
}
}
// Check for a TUF laptop LED. Assume there is only ever one.
if let Ok(kbd_backlight) = KeyboardBacklight::new() {
if kbd_backlight.has_kbd_rgb_mode() {
// Extra sure double-check that this isn't a laptop with crap
// ACPI with borked return on the TUF rgb methods
let dmi = DMIID::new().unwrap_or_default();
info!("Found a TUF with product family: {}", dmi.product_family);
info!("and board name: {}", dmi.board_name);
if dmi.product_family.contains("TUF") {
info!("AuraControl found a TUF laptop keyboard");
let ctrl = CtrlKbdLed {
led_type: AuraDeviceType::LaptopKeyboardTuf,
led_node: LEDNode::KbdLed(kbd_backlight),
supported_data: LedSupportData::get_data("tuf"),
per_key_mode_active: false,
config: AuraConfig::load_and_update_config("tuf"),
dbus_path: dbus_path_for_tuf(),
};
devices.push(ctrl);
}
}
} else {
let dmi = DMIID::new().unwrap_or_default();
warn!("No asus::kbd_backlight found for {} ??", dmi.product_family);
}
info!("Found {} Aura devices", devices.len());
Ok(devices)
}
/// The generated data from this function has a default config. This config
/// should be overwritten. The reason for the default config is because
/// of async issues between this and udev/hidraw
fn from_hidraw(device: HidRaw, dbus_path: OwnedObjectPath) -> Result<Self, RogError> {
let rgb_led = KeyboardBacklight::new()
.map_err(|e| {
log::error!(
"{} is missing a keyboard backlight brightness control: {e:?}",
device.prod_id()
);
})
.ok();
let prod_id = AuraDeviceType::from(device.prod_id());
if prod_id == AuraDeviceType::Unknown {
log::error!("{} is AuraDevice::Unknown", device.prod_id());
return Err(RogError::NoAuraNode);
}
// New loads data from the DB also
// let config = Self::init_config(prod_id, data);
let data = LedSupportData::get_data(device.prod_id());
let ctrl = CtrlKbdLed {
led_type: prod_id,
led_node: LEDNode::Rog(rgb_led, device),
supported_data: data.clone(),
per_key_mode_active: false,
config: AuraConfig::default(),
dbus_path,
};
Ok(ctrl)
}
pub(super) fn fix_ally_power(&mut self) -> Result<(), RogError> {
if self.led_type == AuraDeviceType::Ally {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
if self.config.ally_fix.is_none() {
let msg = [0x5d, 0xbd, 0x01, 0xff, 0xff, 0xff, 0xff];
hid_raw.write_bytes(&msg)?;
info!("Reset Ally power settings to base");
self.config.ally_fix = Some(true);
}
self.config.write();
}
}
Ok(())
}
/// Set combination state for boot animation/sleep animation/all leds/keys
/// leds/side leds LED active
pub(super) fn set_power_states(&mut self) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &mut self.led_node {
// TODO: tuf bool array
let buf = self.config.enabled.to_bytes(self.led_type);
platform.set_kbd_rgb_state(&buf)?;
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
if let Some(p) = self.config.enabled.states.first() {
if p.zone == PowerZones::Ally {
let msg = [0x5d, 0xd1, 0x09, 0x01, p.new_to_byte() as u8, 0x0, 0x0];
hid_raw.write_bytes(&msg)?;
return Ok(());
}
}
let bytes = self.config.enabled.to_bytes(self.led_type);
let msg = [0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], bytes[3]];
hid_raw.write_bytes(&msg)?;
}
Ok(())
}
/// Write an effect block. This is for per-key, but can be repurposed to
/// write the raw factory mode packets - when doing this it is expected that
/// only the first `Vec` (`effect[0]`) is valid.
pub fn write_effect_block(&mut self, effect: &UsbPackets) -> Result<(), RogError> {
if self.config.brightness == LedBrightness::Off {
self.config.brightness = LedBrightness::Med;
self.config.write();
}
let pkt_type = effect[0][1];
const PER_KEY_TYPE: u8 = 0xbc;
if pkt_type != PER_KEY_TYPE {
self.per_key_mode_active = false;
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
hid_raw.write_bytes(&effect[0])?;
hid_raw.write_bytes(&LED_SET)?;
// hid_raw.write_bytes(&LED_APPLY)?;
}
} else {
if !self.per_key_mode_active {
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let init = LedUsbPackets::get_init_msg();
hid_raw.write_bytes(&init)?;
}
self.per_key_mode_active = true;
}
if let LEDNode::Rog(_, hid_raw) = &self.led_node {
for row in effect.iter() {
hid_raw.write_bytes(row)?;
}
} else if let LEDNode::KbdLed(tuf) = &self.led_node {
for row in effect.iter() {
let r = row[9];
let g = row[10];
let b = row[11];
tuf.set_kbd_rgb_mode(&[0, 0, r, g, b, 0])?;
}
}
}
Ok(())
}
/// Write the AuraEffect to the device
pub fn write_effect_and_apply(&mut self, mode: &AuraEffect) -> Result<(), RogError> {
if let LEDNode::KbdLed(platform) = &self.led_node {
let buf = [
1,
mode.mode as u8,
mode.colour1.r,
mode.colour1.g,
mode.colour1.b,
mode.speed as u8,
];
platform.set_kbd_rgb_mode(&buf)?;
} else if let LEDNode::Rog(_, hid_raw) = &self.led_node {
let bytes: [u8; LED_MSG_LEN] = mode.into();
hid_raw.write_bytes(&bytes)?;
hid_raw.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
hid_raw.write_bytes(&LED_APPLY)?;
} else {
return Err(RogError::NoAuraKeyboard);
}
self.per_key_mode_active = false;
Ok(())
}
pub(super) fn write_current_config_mode(&mut self) -> Result<(), RogError> {
if self.config.multizone_on {
let mode = self.config.current_mode;
let mut create = false;
// There is no multizone config for this mode so create one here
// using the colours of rainbow if it exists, or first available
// mode, or random
if self.config.multizone.is_none() {
create = true;
} else if let Some(multizones) = self.config.multizone.as_ref() {
if !multizones.contains_key(&mode) {
create = true;
}
}
if create {
info!("No user-set config for zone founding, attempting a default");
self.config.create_multizone_default(&self.supported_data)?;
}
if let Some(multizones) = self.config.multizone.as_mut() {
if let Some(set) = multizones.get(&mode) {
for mode in set.clone() {
self.write_effect_and_apply(&mode)?;
}
}
}
} else {
let mode = self.config.current_mode;
if let Some(effect) = self.config.builtins.get(&mode).cloned() {
self.write_effect_and_apply(&effect)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rog_aura::aura_detection::LedSupportData;
use rog_aura::{AuraDeviceType, AuraModeNum, AuraZone, PowerZones};
use rog_platform::hid_raw::HidRaw;
use rog_platform::keyboard_led::KeyboardBacklight;
use zbus::zvariant::OwnedObjectPath;
use super::CtrlKbdLed;
use crate::ctrl_aura::config::AuraConfig;
use crate::ctrl_aura::controller::LEDNode;
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
fn create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopKeyboard2021,
led_node: LEDNode::Rog(
Some(KeyboardBacklight::default()),
HidRaw::new("19b6").unwrap(),
),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
assert!(controller
.config
.create_multizone_default(&controller.supported_data)
.is_err());
assert!(controller.config.multizone.is_none());
controller.supported_data.basic_zones.push(AuraZone::Key1);
controller.supported_data.basic_zones.push(AuraZone::Key2);
assert!(controller
.config
.create_multizone_default(&controller.supported_data)
.is_ok());
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
#[test]
#[ignore = "Unable to run in CI as the HIDRAW device is required"]
// TODO: use sim device
fn next_mode_create_multizone_if_no_config() {
// Checking to ensure set_mode errors when unsupported modes are tried
let config = AuraConfig::new("19b6");
let supported_basic_modes = LedSupportData {
device_name: String::new(),
product_id: String::new(),
layout_name: "ga401".to_owned(),
basic_modes: vec![AuraModeNum::Static],
basic_zones: vec![AuraZone::Key1, AuraZone::Key2],
advanced_type: rog_aura::keyboard::AdvancedAuraType::None,
power_zones: vec![PowerZones::Keyboard, PowerZones::RearGlow],
};
let mut controller = CtrlKbdLed {
led_type: AuraDeviceType::LaptopKeyboard2021,
led_node: LEDNode::Rog(
Some(KeyboardBacklight::default()),
HidRaw::new("19b6").unwrap(),
),
supported_data: supported_basic_modes,
per_key_mode_active: false,
config,
dbus_path: OwnedObjectPath::default(),
};
assert!(controller.config.multizone.is_none());
controller.config.multizone_on = true;
// This is called in toggle_mode. It will error here because we have no
// keyboard node in tests.
assert_eq!(
controller
.write_current_config_mode()
.unwrap_err()
.to_string(),
"No supported Aura keyboard"
);
assert!(controller.config.multizone.is_some());
let m = controller.config.multizone.unwrap();
assert!(m.contains_key(&AuraModeNum::Static));
let e = m.get(&AuraModeNum::Static).unwrap();
assert_eq!(e.len(), 2);
assert_eq!(e[0].zone, AuraZone::Key1);
assert_eq!(e[1].zone, AuraZone::Key2);
}
}

View File

@@ -1,138 +0,0 @@
// Plan:
// - Manager has udev monitor on USB looking for ROG devices
// - If a device is found, add it to watch
// - Add it to Zbus server
// - If udev sees device removed then remove the zbus path
use std::collections::HashSet;
use log::{error, info, warn};
use mio::{Events, Interest, Poll, Token};
use udev::{Device, MonitorBuilder};
use zbus::object_server::SignalEmitter;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
use zbus::Connection;
use crate::ctrl_aura::controller::CtrlKbdLed;
use crate::ctrl_aura::trait_impls::{CtrlAuraZbus, AURA_ZBUS_PATH};
use crate::error::RogError;
use crate::{CtrlTask, Reloadable};
pub struct AuraManager {
_connection: Connection,
}
impl AuraManager {
pub async fn new(connection: Connection) -> Result<Self, RogError> {
let conn_copy = connection.clone();
let mut interfaces = HashSet::new();
// Do the initial keyboard detection:
let all = CtrlKbdLed::find_all()?;
for ctrl in all {
let path = ctrl.dbus_path.clone();
interfaces.insert(path.clone()); // ensure we record the initial stuff
let sig_ctx = CtrlAuraZbus::signal_context(&connection)?;
let sig_ctx2 = sig_ctx.clone();
let zbus = CtrlAuraZbus::new(ctrl, sig_ctx);
start_tasks(zbus, connection.clone(), sig_ctx2, path).await?;
}
let manager = Self {
_connection: connection,
};
// detect all plugged in aura devices (eventually)
// only USB devices are detected for here
std::thread::spawn(move || {
let mut monitor = MonitorBuilder::new()?.match_subsystem("hidraw")?.listen()?;
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
poll.registry()
.register(&mut monitor, Token(0), Interest::READABLE)?;
loop {
if poll.poll(&mut events, None).is_err() {
continue;
}
for event in monitor.iter() {
let action = event.action().unwrap_or_default();
if let Some(parent) =
event.parent_with_subsystem_devtype("usb", "usb_device")?
{
if action == "remove" {
if let Some(path) = dbus_path_for_dev(&parent) {
if interfaces.remove(&path) {
info!("AuraManager removing: {path:?}");
let conn_copy = conn_copy.clone();
tokio::spawn(async move {
let res = conn_copy
.object_server()
.remove::<CtrlAuraZbus, _>(&path)
.await
.map_err(|e| {
error!("Failed to remove {path:?}, {e:?}");
e
})?;
info!("AuraManager removed: {path:?}, {res}");
Ok::<(), RogError>(())
});
}
}
} else if action == "add" {
if let Ok(Some(ctrl)) =
CtrlKbdLed::maybe_device(event.device(), &mut interfaces)
{
ctrl.add_to_dbus_and_start(&mut interfaces, conn_copy.clone())
.map_err(|e| {
error!("Couldn't start aura device on dbus: {e:?}")
})
.ok();
}
};
}
}
}
// Required for return type on spawn
#[allow(unreachable_code)]
Ok::<(), RogError>(())
});
Ok(manager)
}
}
pub(crate) fn dbus_path_for_dev(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(filename) = super::filename_partial(parent) {
return Some(
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/{filename}")).into(),
);
}
None
}
pub(crate) fn dbus_path_for_tuf() -> OwnedObjectPath {
ObjectPath::from_str_unchecked(&format!("{AURA_ZBUS_PATH}/tuf")).into()
}
pub async fn start_tasks(
mut zbus: CtrlAuraZbus,
connection: Connection,
_signal_ctx: SignalEmitter<'static>,
path: OwnedObjectPath,
) -> Result<(), RogError> {
// let task = zbus.clone();
// let signal_ctx = signal_ctx.clone();
zbus.reload()
.await
.unwrap_or_else(|err| warn!("Controller error: {}", err));
connection
.object_server()
.at(path.clone(), zbus)
.await
.map_err(|e| error!("Couldn't add server at path: {path}, {e:?}"))
.ok();
// TODO: skip this until we keep handles to tasks so they can be killed
// task.create_tasks(signal_ctx).await
Ok(())
}

View File

@@ -1,34 +0,0 @@
use log::warn;
use udev::Device;
use zbus::zvariant::{ObjectPath, OwnedObjectPath};
pub mod config;
pub mod controller;
pub mod manager;
/// Implements `CtrlTask`, `Reloadable`, `ZbusRun`
pub mod trait_impls;
/// Returns only the Device details concatenated in a form usable for
/// adding/appending to a filename
pub(super) fn filename_partial(parent: &Device) -> Option<OwnedObjectPath> {
if let Some(id_product) = parent.attribute_value("idProduct") {
let id_product = id_product.to_string_lossy();
let mut path = if let Some(devnum) = parent.attribute_value("devnum") {
let devnum = devnum.to_string_lossy();
if let Some(devpath) = parent.attribute_value("devpath") {
let devpath = devpath.to_string_lossy();
format!("{id_product}_{devnum}_{devpath}")
} else {
format!("{id_product}_{devnum}")
}
} else {
format!("{id_product}")
};
if path.contains('.') {
warn!("dbus path for {id_product} contains `.`, removing");
path.replace('.', "").clone_into(&mut path);
}
return Some(ObjectPath::from_str_unchecked(&path).into());
}
None
}

View File

@@ -1,318 +0,0 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use config_traits::StdConfig;
use log::{debug, error, info, warn};
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::export::futures_util::lock::{Mutex, MutexGuard};
use zbus::export::futures_util::StreamExt;
use zbus::fdo::Error as ZbErr;
use zbus::interface;
use zbus::object_server::SignalEmitter;
use super::controller::CtrlKbdLed;
use crate::error::RogError;
use crate::CtrlTask;
pub const AURA_ZBUS_NAME: &str = "Aura";
pub const AURA_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlAuraZbus(Arc<Mutex<CtrlKbdLed>>, SignalEmitter<'static>);
impl CtrlAuraZbus {
pub fn new(controller: CtrlKbdLed, signal: SignalEmitter<'static>) -> Self {
Self(Arc::new(Mutex::new(controller)), signal)
}
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
let bright = lock.led_node.get_brightness().unwrap_or_default();
lock.config.read();
lock.config.brightness = bright.into();
lock.config.write();
Ok(())
}
}
/// The main interface for changing, reading, or notfying
///
/// LED commands are split between Brightness, Modes, Per-Key
#[interface(name = "org.asuslinux.Aura")]
impl CtrlAuraZbus {
/// Return the device type for this Aura keyboard
#[zbus(property)]
async fn device_type(&self) -> AuraDeviceType {
let ctrl = self.0.lock().await;
ctrl.led_type
}
/// Return the current LED brightness
#[zbus(property)]
async fn brightness(&self) -> Result<LedBrightness, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.get_brightness().map(|n| n.into())?)
}
/// Set the keyboard brightness level (0-3)
#[zbus(property)]
async fn set_brightness(&mut self, brightness: LedBrightness) -> Result<(), ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.led_node.set_brightness(brightness.into())?)
}
/// Total levels of brightness available
#[zbus(property)]
async fn supported_brightness(&self) -> Vec<LedBrightness> {
vec![
LedBrightness::Off,
LedBrightness::Low,
LedBrightness::Med,
LedBrightness::High,
]
}
/// The total available modes
#[zbus(property)]
async fn supported_basic_modes(&self) -> Result<Vec<AuraModeNum>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.config.builtins.keys().cloned().collect())
}
#[zbus(property)]
async fn supported_basic_zones(&self) -> Result<Vec<AuraZone>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.basic_zones.clone())
}
#[zbus(property)]
async fn supported_power_zones(&self) -> Result<Vec<PowerZones>, ZbErr> {
let ctrl = self.0.lock().await;
Ok(ctrl.supported_data.power_zones.clone())
}
/// The current mode data
#[zbus(property)]
async fn led_mode(&self) -> Result<AuraModeNum, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
// let ctrl = self.0.lock().await;
// Ok(ctrl.config.current_mode)
if let Some(ctrl) = self.0.try_lock() {
Ok(ctrl.config.current_mode)
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode(&mut self, num: AuraModeNum) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.config.current_mode = num;
ctrl.write_current_config_mode()?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
if ctrl.led_node.has_brightness_control() {
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
}
ctrl.config.write();
self.led_mode_changed(&self.1).await.ok();
self.led_mode_data_changed(&self.1).await.ok();
Ok(())
}
/// The current mode data
#[zbus(property)]
async fn led_mode_data(&self) -> Result<AuraEffect, ZbErr> {
// entirely possible to deadlock here, so use try instead of lock()
if let Some(ctrl) = self.0.try_lock() {
let mode = ctrl.config.current_mode;
match ctrl.config.builtins.get(&mode) {
Some(effect) => Ok(effect.clone()),
None => Err(ZbErr::Failed("Could not get the current effect".into())),
}
} else {
Err(ZbErr::Failed("Aura control couldn't lock self".to_string()))
}
}
/// Set an Aura effect if the effect mode or zone is supported.
///
/// On success the aura config file is read to refresh cached values, then
/// the effect is stored and config written to disk.
#[zbus(property)]
async fn set_led_mode_data(&mut self, effect: AuraEffect) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
if !ctrl.supported_data.basic_modes.contains(&effect.mode)
|| effect.zone != AuraZone::None
&& !ctrl.supported_data.basic_zones.contains(&effect.zone)
{
return Err(ZbErr::NotSupported(format!(
"The Aura effect is not supported: {effect:?}"
)));
}
ctrl.write_effect_and_apply(&effect)?;
if ctrl.config.brightness == LedBrightness::Off {
ctrl.config.brightness = LedBrightness::Med;
}
if ctrl.led_node.has_brightness_control() {
ctrl.led_node
.set_brightness(ctrl.config.brightness.into())?;
}
ctrl.config.set_builtin(effect);
ctrl.config.write();
self.led_mode_changed(&self.1).await.ok();
Ok(())
}
/// Get the data set for every mode available
async fn all_mode_data(&self) -> BTreeMap<AuraModeNum, AuraEffect> {
let ctrl = self.0.lock().await;
ctrl.config.builtins.clone()
}
// As property doesn't work for AuraPowerDev (complexity of serialization?)
#[zbus(property)]
async fn led_power(&self) -> LaptopAuraPower {
let ctrl = self.0.lock().await;
ctrl.config.enabled.clone()
}
/// Set a variety of states, input is array of enum.
/// `enabled` sets if the sent array should be disabled or enabled
///
/// For Modern ROG devices the "enabled" flag is ignored.
#[zbus(property)]
async fn set_led_power(&mut self, options: LaptopAuraPower) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
for opt in options.states {
let zone = opt.zone;
for config in ctrl.config.enabled.states.iter_mut() {
if config.zone == zone {
*config = opt;
}
}
}
ctrl.config.write();
Ok(ctrl.set_power_states().map_err(|e| {
warn!("{}", e);
e
})?)
}
/// On machine that have some form of either per-key keyboard or per-zone
/// this can be used to write custom effects over dbus. The input is a
/// nested `Vec<Vec<8>>` where `Vec<u8>` is a raw USB packet
async fn direct_addressing_raw(&self, data: UsbPackets) -> Result<(), ZbErr> {
let mut ctrl = self.0.lock().await;
ctrl.write_effect_block(&data)?;
Ok(())
}
}
impl CtrlTask for CtrlAuraZbus {
fn zbus_path() -> &'static str {
"/org/asuslinux"
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
let load_save =
|start: bool, mut lock: MutexGuard<'_, CtrlKbdLed>| -> Result<(), RogError> {
// If waking up
if !start {
info!("CtrlKbdLedTask reloading brightness and modes");
if lock.led_node.has_brightness_control() {
lock.led_node
.set_brightness(lock.config.brightness.into())
.map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
lock.write_current_config_mode().map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
} else if start {
Self::update_config(&mut lock).map_err(|e| {
error!("CtrlKbdLedTask: {e}");
e
})?;
}
Ok(())
};
let inner1 = self.0.clone();
let inner3 = self.0.clone();
self.create_sys_event_tasks(
move |sleeping| {
let inner1 = inner1.clone();
async move {
let lock = inner1.lock().await;
load_save(sleeping, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_shutting_down| {
let inner3 = inner3.clone();
async move {
let lock = inner3.lock().await;
load_save(false, lock).unwrap(); // unwrap as we want to
// bomb out of the task
}
},
move |_lid_closed| {
// on lid change
async move {}
},
move |_power_plugged| {
// power change
async move {}
},
)
.await;
let ctrl2 = self.0.clone();
let ctrl = self.0.lock().await;
if ctrl.led_node.has_brightness_control() {
let watch = ctrl.led_node.monitor_brightness()?;
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.into_event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Some(lock) = ctrl2.try_lock() {
load_save(true, lock).unwrap(); // unwrap as we want
// to
// bomb out of the
// task
}
})
.await;
});
}
Ok(())
}
}
impl crate::Reloadable for CtrlAuraZbus {
async fn reload(&mut self) -> Result<(), RogError> {
let mut ctrl = self.0.lock().await;
ctrl.fix_ally_power()?;
debug!("reloading keyboard mode");
ctrl.write_current_config_mode()?;
debug!("reloading power states");
ctrl.set_power_states().map_err(|err| warn!("{err}")).ok();
Ok(())
}
}

View File

@@ -1,102 +0,0 @@
pub mod config;
pub mod trait_impls;
use config_traits::{StdConfig, StdConfigLoad};
use log::info;
use rog_platform::hid_raw::HidRaw;
use rog_platform::usb_raw::USBRaw;
use rog_slash::error::SlashError;
use rog_slash::usb::{get_maybe_slash_type, pkt_set_mode, pkt_set_options, pkts_for_init};
use rog_slash::{SlashMode, SlashType};
use crate::ctrl_slash::config::SlashConfig;
use crate::error::RogError;
enum Node {
Usb(USBRaw),
Hid(HidRaw),
}
impl Node {
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
// TODO: map and pass on errors
match self {
Node::Usb(u) => {
u.write_bytes(message).ok();
}
Node::Hid(h) => {
h.write_bytes(message).ok();
}
}
Ok(())
}
}
pub struct CtrlSlash {
node: Node,
config: SlashConfig,
}
impl CtrlSlash {
#[inline]
pub fn new() -> Result<CtrlSlash, RogError> {
let slash_type = get_maybe_slash_type()?;
if matches!(slash_type, SlashType::Unsupported) {
info!("No Slash capable laptop found");
return Err(RogError::Slash(SlashError::NoDevice));
}
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
let node = if usb.is_some() {
info!("Slash is using raw USB");
unsafe { Node::Usb(usb.unwrap_unchecked()) }
} else if hid.is_some() {
info!("Slash is using HIDRAW");
unsafe { Node::Hid(hid.unwrap_unchecked()) }
} else {
return Err(RogError::Slash(SlashError::NoDevice));
};
let ctrl = CtrlSlash {
node,
config: SlashConfig::new().load(),
};
ctrl.do_initialization()?;
Ok(ctrl)
}
fn do_initialization(&self) -> Result<(), RogError> {
let init_packets = pkts_for_init();
self.node.write_bytes(&init_packets[0])?;
self.node.write_bytes(&init_packets[1])?;
// Apply config upon initialization
let option_packets = pkt_set_options(
self.config.slash_enabled,
self.config.slash_brightness,
self.config.slash_interval,
);
self.node.write_bytes(&option_packets)?;
let mode_packets = pkt_set_mode(self.config.slash_mode);
self.node.write_bytes(&mode_packets[0])?;
self.node.write_bytes(&mode_packets[1])?;
Ok(())
}
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
let command_packets = pkt_set_options(enabled, brightness, interval);
self.node.write_bytes(&command_packets)?;
Ok(())
}
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
let command_packets = pkt_set_mode(slash_mode);
self.node.write_bytes(&command_packets[0])?;
self.node.write_bytes(&command_packets[1])?;
Ok(())
}
}

View File

@@ -1,166 +0,0 @@
use std::sync::Arc;
use config_traits::StdConfig;
use log::warn;
use rog_slash::usb::{pkt_set_mode, pkt_set_options};
use rog_slash::{DeviceState, SlashMode};
use zbus::export::futures_util::lock::Mutex;
use zbus::object_server::SignalEmitter;
use zbus::{interface, Connection};
use crate::ctrl_slash::CtrlSlash;
use crate::error::RogError;
pub const SLASH_ZBUS_NAME: &str = "Slash";
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
#[derive(Clone)]
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
/// The struct with the main dbus methods requires this trait
impl crate::ZbusRun for CtrlSlashZbus {
async fn add_to_server(self, server: &mut Connection) {
Self::add_to_server_helper(self, SLASH_ZBUS_PATH, server).await;
}
}
#[interface(name = "org.asuslinux.Slash")]
impl CtrlSlashZbus {
/// Get enabled or not
#[zbus(property)]
async fn enabled(&self) -> bool {
let lock = self.0.lock().await;
lock.config.slash_enabled
}
/// Set enabled true or false
#[zbus(property)]
async fn set_enabled(&self, enabled: bool) {
let mut lock = self.0.lock().await;
let brightness = if enabled && lock.config.slash_brightness == 0 {
0x88
} else {
lock.config.slash_brightness
};
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
/// Get brightness level
#[zbus(property)]
async fn brightness(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_brightness
}
/// Set brightness level
#[zbus(property)]
async fn set_brightness(&self, brightness: u8) {
let mut lock = self.0.lock().await;
let enabled = brightness > 0;
lock.node
.write_bytes(&pkt_set_options(
enabled,
brightness,
lock.config.slash_interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_enabled = enabled;
lock.config.slash_brightness = brightness;
lock.config.write();
}
#[zbus(property)]
async fn interval(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_interval(&self, interval: u8) {
let mut lock = self.0.lock().await;
lock.node
.write_bytes(&pkt_set_options(
lock.config.slash_enabled,
lock.config.slash_brightness,
interval,
))
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_interval = interval;
lock.config.write();
}
#[zbus(property)]
async fn slash_mode(&self) -> u8 {
let lock = self.0.lock().await;
lock.config.slash_interval
}
/// Set interval between slash animations (0-255)
#[zbus(property)]
async fn set_slash_mode(&self, slash_mode: SlashMode) {
let mut lock = self.0.lock().await;
let command_packets = pkt_set_mode(slash_mode);
lock.node
.write_bytes(&command_packets[0])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.node
.write_bytes(&command_packets[1])
.map_err(|err| {
warn!("ctrl_slash::set_options {}", err);
})
.ok();
lock.config.slash_mode = slash_mode;
lock.config.write();
}
/// Get the device state as stored by asusd
// #[zbus(property)]
async fn device_state(&self) -> DeviceState {
let lock = self.0.lock().await;
DeviceState::from(&lock.config)
}
}
impl crate::CtrlTask for CtrlSlashZbus {
fn zbus_path() -> &'static str {
SLASH_ZBUS_PATH
}
async fn create_tasks(&self, _: SignalEmitter<'static>) -> Result<(), RogError> {
Ok(())
}
}
impl crate::Reloadable for CtrlSlashZbus {
async fn reload(&mut self) -> Result<(), RogError> {
Ok(())
}
}

View File

@@ -4,14 +4,10 @@ use std::sync::Arc;
use ::zbus::export::futures_util::lock::Mutex; use ::zbus::export::futures_util::lock::Mutex;
use ::zbus::Connection; use ::zbus::Connection;
use asusd::aura_manager::DeviceManager;
use asusd::config::Config; use asusd::config::Config;
use asusd::ctrl_anime::trait_impls::CtrlAnimeZbus;
use asusd::ctrl_anime::CtrlAnime;
use asusd::ctrl_aura::manager::AuraManager;
use asusd::ctrl_fancurves::CtrlFanCurveZbus; use asusd::ctrl_fancurves::CtrlFanCurveZbus;
use asusd::ctrl_platform::CtrlPlatform; use asusd::ctrl_platform::CtrlPlatform;
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
use asusd::ctrl_slash::CtrlSlash;
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME}; use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
use config_traits::{StdConfig, StdConfigLoad1}; use config_traits::{StdConfig, StdConfigLoad1};
use log::{error, info}; use log::{error, info};
@@ -97,33 +93,7 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
} }
} }
match CtrlAnime::new() { let _ = DeviceManager::new(connection.clone()).await?;
Ok(ctrl) => {
let zbus = CtrlAnimeZbus(Arc::new(Mutex::new(ctrl)));
let sig_ctx = CtrlAnimeZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
let _ = AuraManager::new(connection.clone()).await?;
match CtrlSlash::new() {
Ok(ctrl) => {
let zbus = CtrlSlashZbus(Arc::new(Mutex::new(ctrl)));
// Currently, the Slash has no need for a loop watching power events, however,
// it could be cool to have the slash do some power-on/off animation
// (It has a built-in power on animation which plays when u plug in the power
// supply)
let sig_ctx = CtrlSlashZbus::signal_context(&connection)?;
start_tasks(zbus, &mut connection, sig_ctx).await?;
}
Err(err) => {
info!("AniMe control: {}", err);
}
}
// Request dbus name after finishing initalizing all functions // Request dbus name after finishing initalizing all functions
connection.request_name(DBUS_NAME).await?; connection.request_name(DBUS_NAME).await?;

View File

@@ -1,17 +1,16 @@
#![deny(unused_must_use)] #![deny(unused_must_use)]
/// Configuration loading, saving /// Configuration loading, saving
pub mod config; pub mod config;
/// Control of anime matrix display
pub mod ctrl_anime;
/// Keyboard LED brightness control, RGB, and LED display modes
pub mod ctrl_aura;
/// Control platform profiles + fan-curves if available /// Control platform profiles + fan-curves if available
pub mod ctrl_fancurves; pub mod ctrl_fancurves;
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode /// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
pub mod ctrl_platform; pub mod ctrl_platform;
/// Control of Slash led bar
pub mod ctrl_slash;
pub mod aura_anime;
pub mod aura_laptop;
pub mod aura_manager;
pub mod aura_slash;
pub mod aura_types;
pub mod error; pub mod error;
use std::future::Future; use std::future::Future;

View File

@@ -1,4 +1,4 @@
use log::{info, warn}; use log::warn;
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Clone)]
pub struct DMIID { pub struct DMIID {
@@ -33,8 +33,6 @@ impl DMIID {
})?; })?;
if let Some(device) = (result).next() { if let Some(device) = (result).next() {
info!("Found dmi ID info at {:?}", device.sysname());
return Ok(Self { return Ok(Self {
id_model: device id_model: device
.property_value("ID_MODEL") .property_value("ID_MODEL")

View File

@@ -3,6 +3,7 @@ use std::str::FromStr;
use std::thread::sleep; use std::thread::sleep;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use dmi_id::DMIID;
use log::info; use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
@@ -57,11 +58,12 @@ pub struct DeviceState {
#[typeshare] #[typeshare]
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))] #[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum AnimeType { pub enum AnimeType {
GA401, GA401,
GA402, GA402,
GU604, GU604,
#[default]
Unsupported, Unsupported,
} }
@@ -79,6 +81,19 @@ impl FromStr for AnimeType {
} }
impl AnimeType { impl AnimeType {
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") {
AnimeType::GA402
} else if board_name.contains("GU604V") {
AnimeType::GU604
} else {
AnimeType::Unsupported
}
}
/// The width of diagonal images /// The width of diagonal images
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
match self { match self {

View File

@@ -247,19 +247,19 @@ impl From<AnimShutdown> for i32 {
/// ///
/// The currently known USB device is `19b6`. /// The currently known USB device is `19b6`.
#[inline] #[inline]
pub fn get_maybe_anime_type() -> Result<AnimeType, AnimeError> { pub fn get_anime_type() -> AnimeType {
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name; let board_name = dmi.board_name;
if board_name.contains("GA401I") || board_name.contains("GA401Q") { if board_name.contains("GA401I") || board_name.contains("GA401Q") {
return Ok(AnimeType::GA401); AnimeType::GA401
} else if board_name.contains("GA402R") || board_name.contains("GA402X") { } else if board_name.contains("GA402R") || board_name.contains("GA402X") {
return Ok(AnimeType::GA402); AnimeType::GA402
} else if board_name.contains("GU604V") { } else if board_name.contains("GU604V") {
return Ok(AnimeType::GU604); AnimeType::GU604
} else {
AnimeType::Unsupported
} }
log::warn!("AniMe Matrix device found but could be a slash");
Ok(AnimeType::Unsupported)
} }
/// Get the two device initialization packets. These are required for device /// Get the two device initialization packets. These are required for device

View File

@@ -656,6 +656,15 @@
advanced_type: Zoned([SingleZone]), advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard], power_zones: [Keyboard],
), ),
(
device_name: "GA605W",
product_id: "",
layout_name: "ga401q",
basic_modes: [Static, Breathe, RainbowCycle, RainbowWave, Pulse],
basic_zones: [],
advanced_type: Zoned([SingleZone]),
power_zones: [Keyboard],
),
( (
device_name: "GV301Q", device_name: "GV301Q",
product_id: "", product_id: "",

View File

@@ -7,7 +7,7 @@ use typeshare::typeshare;
use zbus::zvariant::{OwnedValue, Type, Value}; use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::Error; use crate::error::Error;
use crate::LED_MSG_LEN; use crate::AURA_LAPTOP_LED_MSG_LEN;
#[typeshare] #[typeshare]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
@@ -552,9 +552,9 @@ impl AuraEffect {
/// |---|---|-----|-----|---------|------|----------|---|-----------| /// |---|---|-----|-----|---------|------|----------|---|-----------|
/// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 | /// |5d |b3 |Zone |Mode |Colour 1 |Speed |Direction |00 |Colour 2 |
/// ``` /// ```
impl From<&AuraEffect> for [u8; LED_MSG_LEN] { impl From<&AuraEffect> for [u8; AURA_LAPTOP_LED_MSG_LEN] {
fn from(aura: &AuraEffect) -> Self { fn from(aura: &AuraEffect) -> Self {
let mut msg = [0u8; LED_MSG_LEN]; let mut msg = [0u8; AURA_LAPTOP_LED_MSG_LEN];
msg[0] = 0x5d; msg[0] = 0x5d;
msg[1] = 0xb3; msg[1] = 0xb3;
msg[2] = aura.zone as u8; msg[2] = aura.zone as u8;
@@ -573,7 +573,7 @@ impl From<&AuraEffect> for [u8; LED_MSG_LEN] {
impl From<&AuraEffect> for Vec<u8> { impl From<&AuraEffect> for Vec<u8> {
fn from(aura: &AuraEffect) -> Self { fn from(aura: &AuraEffect) -> Self {
let mut msg = vec![0u8; LED_MSG_LEN]; let mut msg = vec![0u8; AURA_LAPTOP_LED_MSG_LEN];
msg[0] = 0x5d; msg[0] = 0x5d;
msg[1] = 0xb3; msg[1] = 0xb3;
msg[2] = aura.zone as u8; msg[2] = aura.zone as u8;
@@ -592,7 +592,9 @@ impl From<&AuraEffect> for Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, LED_MSG_LEN}; use crate::{
AuraEffect, AuraModeNum, AuraZone, Colour, Direction, Speed, AURA_LAPTOP_LED_MSG_LEN,
};
#[test] #[test]
fn check_led_static_packet() { fn check_led_static_packet() {
@@ -608,7 +610,7 @@ mod tests {
speed: Speed::Med, speed: Speed::Med,
direction: Direction::Right, direction: Direction::Right,
}; };
let ar = <[u8; LED_MSG_LEN]>::from(&st); let ar = <[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st);
println!("{:02x?}", ar); println!("{:02x?}", ar);
let check = [ let check = [
@@ -636,7 +638,10 @@ mod tests {
0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x01, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key2; st.zone = AuraZone::Key2;
st.colour1 = Colour { st.colour1 = Colour {
@@ -648,7 +653,10 @@ mod tests {
0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x02, 0x00, 0xff, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key3; st.zone = AuraZone::Key3;
st.colour1 = Colour { st.colour1 = Colour {
@@ -660,7 +668,10 @@ mod tests {
0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Key4; st.zone = AuraZone::Key4;
st.colour1 = Colour { st.colour1 = Colour {
@@ -672,7 +683,10 @@ mod tests {
0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x04, 0x00, 0xff, 0x00, 0xff, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::Logo; st.zone = AuraZone::Logo;
st.colour1 = Colour { st.colour1 = Colour {
@@ -684,7 +698,10 @@ mod tests {
0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x05, 0x00, 0x2c, 0xff, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarLeft; st.zone = AuraZone::BarLeft;
st.colour1 = Colour { st.colour1 = Colour {
@@ -696,7 +713,10 @@ mod tests {
0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x06, 0x00, 0xff, 0x00, 0x00, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.zone = AuraZone::BarRight; st.zone = AuraZone::BarRight;
st.colour1 = Colour { st.colour1 = Colour {
@@ -708,13 +728,19 @@ mod tests {
0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x07, 0x00, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
st.mode = AuraModeNum::RainbowWave; st.mode = AuraModeNum::RainbowWave;
let capture = [ let capture = [
0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0xb3, 0x07, 0x03, 0xff, 0x00, 0xcd, 0xe1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
]; ];
assert_eq!(<[u8; LED_MSG_LEN]>::from(&st)[..9], capture[..9]); assert_eq!(
<[u8; AURA_LAPTOP_LED_MSG_LEN]>::from(&st)[..9],
capture[..9]
);
} }
} }

View File

@@ -12,7 +12,7 @@ pub use breathe::*;
mod static_; mod static_;
pub use static_::*; pub use static_::*;
use crate::keyboard::{KeyLayout, LedCode, LedUsbPackets, UsbPackets}; use crate::keyboard::{AuraLaptopUsbPackets, KeyLayout, LedCode, LedUsbPackets};
use crate::Colour; use crate::Colour;
// static mut RNDINDEX: usize = 0; // static mut RNDINDEX: usize = 0;
@@ -106,7 +106,7 @@ impl AdvancedEffects {
} }
} }
pub fn create_packets(&self) -> UsbPackets { pub fn create_packets(&self) -> AuraLaptopUsbPackets {
let mut usb_packets = if self.zoned { let mut usb_packets = if self.zoned {
// TODO: figure out if that single byte difference for multizone actually // TODO: figure out if that single byte difference for multizone actually
// matters // matters

View File

@@ -195,7 +195,7 @@ impl LedCode {
/// Represents the per-key raw USB packets /// Represents the per-key raw USB packets
#[typeshare] #[typeshare]
pub type UsbPackets = Vec<Vec<u8>>; pub type AuraLaptopUsbPackets = Vec<Vec<u8>>;
/// A `UsbPackets` contains all data to change the full set of keyboard /// A `UsbPackets` contains all data to change the full set of keyboard
/// key colours individually. /// key colours individually.
@@ -209,7 +209,7 @@ pub type UsbPackets = Vec<Vec<u8>>;
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LedUsbPackets { pub struct LedUsbPackets {
/// The packet data used to send data to the USB keyboard /// The packet data used to send data to the USB keyboard
usb_packets: UsbPackets, usb_packets: AuraLaptopUsbPackets,
/// Wether or not this packet collection is zoned. The determines which /// Wether or not this packet collection is zoned. The determines which
/// starting bytes are used and what the indexing is for lightbar RGB /// starting bytes are used and what the indexing is for lightbar RGB
/// colours /// colours
@@ -472,22 +472,22 @@ impl LedUsbPackets {
} }
#[inline] #[inline]
pub fn get(&self) -> UsbPackets { pub fn get(&self) -> AuraLaptopUsbPackets {
self.usb_packets.clone() self.usb_packets.clone()
} }
#[inline] #[inline]
pub fn get_ref(&self) -> &UsbPackets { pub fn get_ref(&self) -> &AuraLaptopUsbPackets {
&self.usb_packets &self.usb_packets
} }
#[inline] #[inline]
pub fn get_mut(&mut self) -> &mut UsbPackets { pub fn get_mut(&mut self) -> &mut AuraLaptopUsbPackets {
&mut self.usb_packets &mut self.usb_packets
} }
} }
impl From<LedUsbPackets> for UsbPackets { impl From<LedUsbPackets> for AuraLaptopUsbPackets {
fn from(k: LedUsbPackets) -> Self { fn from(k: LedUsbPackets) -> Self {
k.usb_packets k.usb_packets
} }
@@ -643,7 +643,7 @@ impl From<&LedCode> for &str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::keyboard::{LedCode, LedUsbPackets, UsbPackets}; use crate::keyboard::{AuraLaptopUsbPackets, LedCode, LedUsbPackets};
macro_rules! colour_check_zoned { macro_rules! colour_check_zoned {
($zone:expr, $pkt_idx_start:expr) => { ($zone:expr, $pkt_idx_start:expr) => {
@@ -653,7 +653,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: UsbPackets = zone.into(); let pkt: AuraLaptopUsbPackets = zone.into();
assert_eq!(pkt[0][$pkt_idx_start], 0xff); assert_eq!(pkt[0][$pkt_idx_start], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff); assert_eq!(pkt[0][$pkt_idx_start + 1], 0xff);
assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff); assert_eq!(pkt[0][$pkt_idx_start + 2], 0xff);
@@ -663,7 +663,7 @@ mod tests {
#[test] #[test]
fn zone_to_packet_check() { fn zone_to_packet_check() {
let zone = LedUsbPackets::new_zoned(true); let zone = LedUsbPackets::new_zoned(true);
let pkt: UsbPackets = zone.into(); let pkt: AuraLaptopUsbPackets = zone.into();
assert_eq!(pkt[0][0], 0x5d); assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc); assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x01); assert_eq!(pkt[0][2], 0x01);
@@ -686,7 +686,7 @@ mod tests {
#[test] #[test]
fn perkey_to_packet_check() { fn perkey_to_packet_check() {
let per_key = LedUsbPackets::new_per_key(); let per_key = LedUsbPackets::new_per_key();
let pkt: UsbPackets = per_key.into(); let pkt: AuraLaptopUsbPackets = per_key.into();
assert_eq!(pkt[0][0], 0x5d); assert_eq!(pkt[0][0], 0x5d);
assert_eq!(pkt[0][1], 0xbc); assert_eq!(pkt[0][1], 0xbc);
assert_eq!(pkt[0][2], 0x00); assert_eq!(pkt[0][2], 0x00);
@@ -712,7 +712,7 @@ mod tests {
c[1] = 255; c[1] = 255;
c[2] = 255; c[2] = 255;
let pkt: UsbPackets = per_key.into(); let pkt: AuraLaptopUsbPackets = per_key.into();
assert_eq!(pkt[5][30], 0xff); // D, red assert_eq!(pkt[5][30], 0xff); // D, red
assert_eq!(pkt[5][31], 0xff); // D assert_eq!(pkt[5][31], 0xff); // D
assert_eq!(pkt[5][32], 0xff); // D assert_eq!(pkt[5][32], 0xff); // D

View File

@@ -24,7 +24,7 @@ pub mod usb;
pub mod keyboard; pub mod keyboard;
pub const LED_MSG_LEN: usize = 17; pub const AURA_LAPTOP_LED_MSG_LEN: usize = 17;
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const RED: Colour = Colour { pub const RED: Colour = Colour {
@@ -108,8 +108,9 @@ impl From<&str> for AuraDeviceType {
"1932" => AuraDeviceType::ScsiExtDisk, "1932" => AuraDeviceType::ScsiExtDisk,
"1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021, "1866" | "18c6" | "1869" | "1854" => Self::LaptopKeyboardPre2021,
"1abe" | "1b4c" => Self::Ally, "1abe" | "1b4c" => Self::Ally,
"19b3" => Self::AnimeOrSlash, "19b3" | "193b" => Self::AnimeOrSlash,
_ => Self::LaptopKeyboard2021, "19b6" => Self::LaptopKeyboard2021,
_ => Self::Unknown,
} }
} }
} }

View File

@@ -1,10 +1,4 @@
// Only these two packets must be 17 bytes // Only these two packets must be 17 bytes
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; pub const AURA_LAPTOP_LED_APPLY: [u8; 17] =
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const AURA_LAPTOP_LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
/// Writes out the correct byte string for brightness
pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
[
0x5a, 0xba, 0xc5, 0xc4, brightness, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
}

View File

@@ -2,7 +2,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-12-17 21:33+0000\n" "POT-Creation-Date: 2024-12-18 23:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -412,198 +412,198 @@ msgctxt "PageAppSettings"
msgid "Enable dGPU notifications" msgid "Enable dGPU notifications"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:51 #: rog-control-center/ui/types/aura_types.slint:52
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Logo" msgid "Logo"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:52 rog-control-center/ui/types/aura_types.slint:62
msgctxt "Aura power zone"
msgid "Keyboard"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:53 rog-control-center/ui/types/aura_types.slint:63 #: rog-control-center/ui/types/aura_types.slint:53 rog-control-center/ui/types/aura_types.slint:63
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Lightbar" msgid "Keyboard"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:54 #: rog-control-center/ui/types/aura_types.slint:54 rog-control-center/ui/types/aura_types.slint:64
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Lid" msgid "Lightbar"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:55 #: rog-control-center/ui/types/aura_types.slint:55
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Lid"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:56
msgctxt "Aura power zone"
msgid "Rear Glow" msgid "Rear Glow"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:56 rog-control-center/ui/types/aura_types.slint:64 #: rog-control-center/ui/types/aura_types.slint:57 rog-control-center/ui/types/aura_types.slint:65
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Keyboard and Lightbar" msgid "Keyboard and Lightbar"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:57 #: rog-control-center/ui/types/aura_types.slint:58
msgctxt "Aura power zone" msgctxt "Aura power zone"
msgid "Ally" msgid "Ally"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:67 #: rog-control-center/ui/types/aura_types.slint:68
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "Off" msgid "Off"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:68
msgctxt "Aura brightness"
msgid "Low"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:69 #: rog-control-center/ui/types/aura_types.slint:69
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "Med" msgid "Low"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:70 #: rog-control-center/ui/types/aura_types.slint:70
msgctxt "Aura brightness" msgctxt "Aura brightness"
msgid "High" msgid "Med"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:75 rog-control-center/ui/types/aura_types.slint:90 #: rog-control-center/ui/types/aura_types.slint:71
msgctxt "Basic aura mode" msgctxt "Aura brightness"
msgid "Static" msgid "High"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:76 rog-control-center/ui/types/aura_types.slint:91 #: rog-control-center/ui/types/aura_types.slint:76 rog-control-center/ui/types/aura_types.slint:91
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Breathe" msgid "Static"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:77 rog-control-center/ui/types/aura_types.slint:92 #: rog-control-center/ui/types/aura_types.slint:77 rog-control-center/ui/types/aura_types.slint:92
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Strobe" msgid "Breathe"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:78 #: rog-control-center/ui/types/aura_types.slint:78 rog-control-center/ui/types/aura_types.slint:93
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Rainbow" msgid "Strobe"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:79 #: rog-control-center/ui/types/aura_types.slint:79
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Star" msgid "Rainbow"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:80 #: rog-control-center/ui/types/aura_types.slint:80
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Rain" msgid "Star"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:81 #: rog-control-center/ui/types/aura_types.slint:81
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Highlight" msgid "Rain"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:82 #: rog-control-center/ui/types/aura_types.slint:82
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Laser" msgid "Highlight"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:83 #: rog-control-center/ui/types/aura_types.slint:83
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Ripple" msgid "Laser"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:84 #: rog-control-center/ui/types/aura_types.slint:84
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Nothing" msgid "Ripple"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:85 #: rog-control-center/ui/types/aura_types.slint:85
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Pulse" msgid "Nothing"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:86 #: rog-control-center/ui/types/aura_types.slint:86
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Comet" msgid "Pulse"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:87 #: rog-control-center/ui/types/aura_types.slint:87
msgctxt "Basic aura mode" msgctxt "Basic aura mode"
msgid "Flash" msgid "Comet"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:99 #: rog-control-center/ui/types/aura_types.slint:88
msgctxt "Aura zone" msgctxt "Basic aura mode"
msgid "None" msgid "Flash"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:100 #: rog-control-center/ui/types/aura_types.slint:100
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key1" msgid "None"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:101 #: rog-control-center/ui/types/aura_types.slint:101
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key2" msgid "Key1"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:102 #: rog-control-center/ui/types/aura_types.slint:102
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key3" msgid "Key2"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:103 #: rog-control-center/ui/types/aura_types.slint:103
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Key4" msgid "Key3"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:104 #: rog-control-center/ui/types/aura_types.slint:104
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Logo" msgid "Key4"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:105 #: rog-control-center/ui/types/aura_types.slint:105
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Lightbar Left" msgid "Logo"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:106 #: rog-control-center/ui/types/aura_types.slint:106
msgctxt "Aura zone" msgctxt "Aura zone"
msgid "Lightbar Right" msgid "Lightbar Left"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:110 #: rog-control-center/ui/types/aura_types.slint:107
msgctxt "Aura direction" msgctxt "Aura zone"
msgid "Right" msgid "Lightbar Right"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:111 #: rog-control-center/ui/types/aura_types.slint:111
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Left" msgid "Right"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:112 #: rog-control-center/ui/types/aura_types.slint:112
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Up" msgid "Left"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:113 #: rog-control-center/ui/types/aura_types.slint:113
msgctxt "Aura direction" msgctxt "Aura direction"
msgid "Down" msgid "Up"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:117 #: rog-control-center/ui/types/aura_types.slint:114
msgctxt "Aura speed" msgctxt "Aura direction"
msgid "Low" msgid "Down"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:118 #: rog-control-center/ui/types/aura_types.slint:118
msgctxt "Aura speed" msgctxt "Aura speed"
msgid "Medium" msgid "Low"
msgstr "" msgstr ""
#: rog-control-center/ui/types/aura_types.slint:119 #: rog-control-center/ui/types/aura_types.slint:119
msgctxt "Aura speed" msgctxt "Aura speed"
msgid "Medium"
msgstr ""
#: rog-control-center/ui/types/aura_types.slint:120
msgctxt "Aura speed"
msgid "High" msgid "High"
msgstr "" msgstr ""

View File

@@ -22,7 +22,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use rog_aura::keyboard::{LaptopAuraPower, UsbPackets}; use rog_aura::keyboard::{AuraLaptopUsbPackets, LaptopAuraPower};
use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones}; use rog_aura::{AuraDeviceType, AuraEffect, AuraModeNum, AuraZone, LedBrightness, PowerZones};
use zbus::blocking::Connection; use zbus::blocking::Connection;
use zbus::{proxy, Result}; use zbus::{proxy, Result};
@@ -39,7 +39,7 @@ pub trait Aura {
fn all_mode_data(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>; fn all_mode_data(&self) -> zbus::Result<BTreeMap<AuraModeNum, AuraEffect>>;
/// DirectAddressingRaw method /// DirectAddressingRaw method
fn direct_addressing_raw(&self, data: UsbPackets) -> zbus::Result<()>; fn direct_addressing_raw(&self, data: AuraLaptopUsbPackets) -> zbus::Result<()>;
/// Brightness property /// Brightness property
#[zbus(property)] #[zbus(property)]
@@ -104,7 +104,7 @@ impl<'a> AuraProxyPerkey<'a> {
/// Intentionally blocks for 10ms after sending to allow the block to /// Intentionally blocks for 10ms after sending to allow the block to
/// be written to the keyboard EC. This should not be async. /// be written to the keyboard EC. This should not be async.
#[inline] #[inline]
pub fn direct_addressing_raw(&self, direct_raw: UsbPackets) -> Result<()> { pub fn direct_addressing_raw(&self, direct_raw: AuraLaptopUsbPackets) -> Result<()> {
self.0.direct_addressing_raw(direct_raw)?; self.0.direct_addressing_raw(direct_raw)?;
std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME)); std::thread::sleep(std::time::Duration::from_millis(BLOCKING_TIME));
Ok(()) Ok(())

View File

@@ -44,40 +44,24 @@ impl HidRaw {
PlatformError::IoPath(endpoint.devpath().to_string_lossy().to_string(), e) PlatformError::IoPath(endpoint.devpath().to_string_lossy().to_string(), e)
})? })?
{ {
if let Some(parent_id) = usb_device.attribute_value("idProduct") {
if parent_id == id_product {
if let Some(dev_node) = endpoint.devnode() { if let Some(dev_node) = endpoint.devnode() {
info!("Using device at: {:?} for hidraw control", dev_node); if let Some(this_id_product) = usb_device.attribute_value("idProduct") {
return Ok(Self { if this_id_product != id_product {
file: RefCell::new(OpenOptions::new().write(true).open(dev_node)?), continue;
devfs_path: dev_node.to_owned(),
prod_id: id_product.to_string(),
syspath: endpoint.syspath().into(),
_device_bcd: usb_device
.attribute_value("bcdDevice")
.unwrap_or_default()
.to_string_lossy()
.parse()
.unwrap_or_default(),
});
} }
}
}
} else {
// Try to see if there is a virtual device created with uhid for testing
let dev_path = endpoint.devpath().to_string_lossy(); let dev_path = endpoint.devpath().to_string_lossy();
if dev_path.contains("virtual") && dev_path.contains(&id_product.to_uppercase()) { if dev_path.contains("virtual") {
if let Some(dev_node) = endpoint.devnode() {
info!( info!(
"Using device at: {:?} for <TODO: label control> control", "Using device at: {:?} for <TODO: label control> control",
dev_node dev_node
); );
}
return Ok(Self { return Ok(Self {
file: RefCell::new(OpenOptions::new().write(true).open(dev_node)?), file: RefCell::new(OpenOptions::new().write(true).open(dev_node)?),
devfs_path: dev_node.to_owned(), devfs_path: dev_node.to_owned(),
prod_id: id_product.to_string(), prod_id: this_id_product.to_string_lossy().into(),
syspath: endpoint.syspath().into(), syspath: endpoint.syspath().into(),
_device_bcd: endpoint _device_bcd: usb_device
.attribute_value("bcdDevice") .attribute_value("bcdDevice")
.unwrap_or_default() .unwrap_or_default()
.to_string_lossy() .to_string_lossy()
@@ -95,19 +79,21 @@ impl HidRaw {
} }
/// Make `HidRaw` device from a udev device /// Make `HidRaw` device from a udev device
pub fn from_device(device: Device) -> Result<Self> { pub fn from_device(endpoint: Device) -> Result<Self> {
if let Some(parent) = device if let Some(parent) = endpoint
.parent_with_subsystem_devtype("usb", "usb_device") .parent_with_subsystem_devtype("usb", "usb_device")
.map_err(|e| PlatformError::IoPath(device.devpath().to_string_lossy().to_string(), e))? .map_err(|e| {
PlatformError::IoPath(endpoint.devpath().to_string_lossy().to_string(), e)
})?
{ {
if let Some(dev_node) = device.devnode() { if let Some(dev_node) = endpoint.devnode() {
if let Some(id_product) = parent.attribute_value("idProduct") { if let Some(id_product) = parent.attribute_value("idProduct") {
return Ok(Self { return Ok(Self {
file: RefCell::new(OpenOptions::new().write(true).open(dev_node)?), file: RefCell::new(OpenOptions::new().write(true).open(dev_node)?),
devfs_path: dev_node.to_owned(), devfs_path: dev_node.to_owned(),
prod_id: id_product.to_string_lossy().into(), prod_id: id_product.to_string_lossy().into(),
syspath: device.syspath().into(), syspath: endpoint.syspath().into(),
_device_bcd: device _device_bcd: endpoint
.attribute_value("bcdDevice") .attribute_value("bcdDevice")
.unwrap_or_default() .unwrap_or_default()
.to_string_lossy() .to_string_lossy()
@@ -129,7 +115,6 @@ impl HidRaw {
/// Write an array of raw bytes to the device using the hidraw interface /// Write an array of raw bytes to the device using the hidraw interface
pub fn write_bytes(&self, message: &[u8]) -> Result<()> { pub fn write_bytes(&self, message: &[u8]) -> Result<()> {
if let Ok(mut file) = self.file.try_borrow_mut() { if let Ok(mut file) = self.file.try_borrow_mut() {
// let mut file = self.file.borrow_mut();
// TODO: re-get the file if error? // TODO: re-get the file if error?
file.write_all(message).map_err(|e| { file.write_all(message).map_err(|e| {
PlatformError::IoPath(self.devfs_path.to_string_lossy().to_string(), e) PlatformError::IoPath(self.devfs_path.to_string_lossy().to_string(), e)

View File

@@ -1,6 +1,7 @@
use std::fmt::Display; use std::fmt::Display;
use std::str::FromStr; use std::str::FromStr;
use dmi_id::DMIID;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use typeshare::typeshare; use typeshare::typeshare;
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
@@ -8,15 +9,50 @@ use zbus::zvariant::Type;
use zbus::zvariant::{OwnedValue, Value}; use zbus::zvariant::{OwnedValue, Value};
use crate::error::SlashError; use crate::error::SlashError;
use crate::usb::{PROD_ID1, PROD_ID1_STR, PROD_ID2, PROD_ID2_STR};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum SlashType { pub enum SlashType {
GA403, GA403,
GA605, GA605,
GU605, GU605,
#[default]
Unsupported, Unsupported,
} }
impl SlashType {
pub const fn prod_id(&self) -> u16 {
match self {
SlashType::GA403 => PROD_ID1,
SlashType::GA605 => PROD_ID2,
SlashType::GU605 => PROD_ID1,
SlashType::Unsupported => 0,
}
}
pub const fn prod_id_str(&self) -> &str {
match self {
SlashType::GA403 => PROD_ID1_STR,
SlashType::GA605 => PROD_ID2_STR,
SlashType::GU605 => PROD_ID1_STR,
SlashType::Unsupported => "",
}
}
pub fn from_dmi() -> Self {
let board_name = DMIID::new().unwrap_or_default().board_name.to_uppercase();
if board_name.contains("GA403") {
SlashType::GA403
} else if board_name.contains("GA605") {
SlashType::GA605
} else if board_name.contains("GU605") {
SlashType::GU605
} else {
SlashType::Unsupported
}
}
}
impl FromStr for SlashType { impl FromStr for SlashType {
type Err = SlashError; type Err = SlashError;

View File

@@ -14,11 +14,16 @@ use dmi_id::DMIID;
use crate::error::SlashError; use crate::error::SlashError;
use crate::{SlashMode, SlashType}; use crate::{SlashMode, SlashType};
const PACKET_SIZE: usize = 128; const PACKET_SIZE: usize = 32;
const DEV_PAGE: u8 = 0x5e; const REPORT_ID_193B: u8 = 0x5e;
const REPORT_ID_19B6: u8 = 0x5d;
pub const VENDOR_ID: u16 = 0x0b05; pub const VENDOR_ID: u16 = 0x0b05;
pub const PROD_ID: u16 = 0x193b;
pub const PROD_ID_STR: &str = "193B"; pub const PROD_ID1: u16 = 0x193b;
pub const PROD_ID1_STR: &str = "193B";
pub const PROD_ID2: u16 = 0x19b6;
pub const PROD_ID2_STR: &str = "19B6";
pub type SlashUsbPacket = [u8; PACKET_SIZE]; pub type SlashUsbPacket = [u8; PACKET_SIZE];
@@ -28,26 +33,40 @@ pub type SlashUsbPacket = [u8; PACKET_SIZE];
/// ///
/// The currently known USB device is `193B`. /// The currently known USB device is `193B`.
#[inline] #[inline]
pub fn get_maybe_slash_type() -> Result<SlashType, SlashError> { pub fn get_slash_type() -> SlashType {
let dmi = DMIID::new().map_err(|_| SlashError::NoDevice)?; // TODO: better error let dmi = DMIID::new()
.map_err(|_| SlashError::NoDevice)
.unwrap_or_default();
let board_name = dmi.board_name; let board_name = dmi.board_name;
if board_name.contains("GA403") { if board_name.contains("GA403") {
return Ok(SlashType::GA403); SlashType::GA403
} else if board_name.contains("GA605") { } else if board_name.contains("GA605") {
return Ok(SlashType::GA605); SlashType::GA605
} else if board_name.contains("GU605") { } else if board_name.contains("GU605") {
return Ok(SlashType::GU605); SlashType::GU605
} else {
SlashType::Unsupported
}
}
pub const fn report_id(slash_type: SlashType) -> u8 {
match slash_type {
SlashType::GA403 => REPORT_ID_193B,
SlashType::GA605 => REPORT_ID_19B6,
SlashType::GU605 => REPORT_ID_193B,
SlashType::Unsupported => REPORT_ID_19B6,
} }
Ok(SlashType::Unsupported)
} }
/// Get the two device initialization packets. These are required for device /// Get the two device initialization packets. These are required for device
/// start after the laptop boots. /// start after the laptop boots.
#[inline] #[inline]
pub const fn pkts_for_init() -> [SlashUsbPacket; 2] { pub fn pkts_for_init(slash_type: SlashType) -> [SlashUsbPacket; 2] {
let report_id = report_id(slash_type);
let mut pkt1 = [0; PACKET_SIZE]; let mut pkt1 = [0; PACKET_SIZE];
pkt1[0] = DEV_PAGE; pkt1[0] = report_id;
pkt1[1] = 0xd7; pkt1[1] = 0xd7;
pkt1[2] = 0x00; pkt1[2] = 0x00;
pkt1[3] = 0x00; pkt1[3] = 0x00;
@@ -55,7 +74,7 @@ pub const fn pkts_for_init() -> [SlashUsbPacket; 2] {
pkt1[5] = 0xac; pkt1[5] = 0xac;
let mut pkt2 = [0; PACKET_SIZE]; let mut pkt2 = [0; PACKET_SIZE];
pkt2[0] = DEV_PAGE; pkt2[0] = report_id;
pkt2[1] = 0xd2; pkt2[1] = 0xd2;
pkt2[2] = 0x02; pkt2[2] = 0x02;
pkt2[3] = 0x01; pkt2[3] = 0x01;
@@ -66,9 +85,9 @@ pub const fn pkts_for_init() -> [SlashUsbPacket; 2] {
} }
#[inline] #[inline]
pub const fn pkt_save() -> SlashUsbPacket { pub const fn pkt_save(slash_type: SlashType) -> SlashUsbPacket {
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE; pkt[0] = report_id(slash_type);
pkt[1] = 0xd4; pkt[1] = 0xd4;
pkt[2] = 0x00; pkt[2] = 0x00;
pkt[3] = 0x00; pkt[3] = 0x00;
@@ -79,16 +98,17 @@ pub const fn pkt_save() -> SlashUsbPacket {
} }
#[inline] #[inline]
pub const fn pkt_set_mode(mode: SlashMode) -> [SlashUsbPacket; 2] { pub const fn pkt_set_mode(slash_type: SlashType, mode: SlashMode) -> [SlashUsbPacket; 2] {
let report_id = report_id(slash_type);
let mut pkt1 = [0; PACKET_SIZE]; let mut pkt1 = [0; PACKET_SIZE];
pkt1[0] = DEV_PAGE; pkt1[0] = report_id;
pkt1[1] = 0x02; pkt1[1] = 0xd2;
pkt1[2] = 0x03; pkt1[2] = 0x03;
pkt1[3] = 0x00; pkt1[3] = 0x00;
pkt1[4] = 0x0c; pkt1[4] = 0x0c;
let mut pkt2 = [0; PACKET_SIZE]; let mut pkt2 = [0; PACKET_SIZE];
pkt2[0] = DEV_PAGE; pkt2[0] = report_id;
pkt2[1] = 0xd3; pkt2[1] = 0xd3;
pkt2[2] = 0x04; pkt2[2] = 0x04;
pkt2[3] = 0x00; pkt2[3] = 0x00;
@@ -96,7 +116,7 @@ pub const fn pkt_set_mode(mode: SlashMode) -> [SlashUsbPacket; 2] {
pkt2[5] = 0x01; pkt2[5] = 0x01;
pkt2[6] = mode as u8; pkt2[6] = mode as u8;
pkt2[7] = 0x02; pkt2[7] = 0x02;
pkt2[8] = 0x19; pkt2[8] = 0x19; // difference, GA605 = 0x10
pkt2[9] = 0x03; pkt2[9] = 0x03;
pkt2[10] = 0x13; pkt2[10] = 0x13;
pkt2[11] = 0x04; pkt2[11] = 0x04;
@@ -110,11 +130,16 @@ pub const fn pkt_set_mode(mode: SlashMode) -> [SlashUsbPacket; 2] {
} }
#[inline] #[inline]
pub const fn pkt_set_options(enabled: bool, brightness: u8, interval: u8) -> SlashUsbPacket { pub const fn pkt_set_options(
slash_type: SlashType,
enabled: bool,
brightness: u8,
interval: u8,
) -> SlashUsbPacket {
let status_byte = if enabled { 0x01 } else { 0x00 }; let status_byte = if enabled { 0x01 } else { 0x00 };
let mut pkt = [0; PACKET_SIZE]; let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE; pkt[0] = report_id(slash_type);
pkt[1] = 0xd3; pkt[1] = 0xd3;
pkt[2] = 0x03; pkt[2] = 0x03;
pkt[3] = 0x01; pkt[3] = 0x01;