From cdc9ca7b588a60b9388f3c5175707be13520ccef Mon Sep 17 00:00:00 2001 From: "jochen@g14" Date: Mon, 25 Mar 2024 01:54:05 +0100 Subject: [PATCH] Try to implement slash bar functionality - part 1 --- Cargo.lock | 33 +++ Cargo.toml | 3 + Makefile | 4 + asusctl/Cargo.toml | 1 + asusctl/src/aura_cli.rs | 7 +- asusctl/src/cli_opts.rs | 3 + asusctl/src/main.rs | 47 ++++ asusctl/src/slash_cli.rs | 19 ++ asusd/Cargo.toml | 1 + asusd/src/config.rs | 8 +- asusd/src/ctrl_anime/config.rs | 8 +- asusd/src/ctrl_aura/config.rs | 8 +- asusd/src/ctrl_aura/controller.rs | 4 +- asusd/src/ctrl_slash/config.rs | 52 ++++ asusd/src/ctrl_slash/mod.rs | 100 +++++++ asusd/src/ctrl_slash/trait_impls.rs | 94 +++++++ asusd/src/daemon.rs | 16 +- asusd/src/lib.rs | 2 + rog-control-center/src/config.rs | 8 +- rog-control-center/src/main.rs | 19 +- rog-dbus/Cargo.toml | 1 + rog-dbus/src/lib.rs | 11 +- rog-dbus/src/zbus_slash.rs | 35 +++ rog-platform/src/usb_raw.rs | 23 +- rog-slash/Cargo.toml | 31 ++- rog-slash/src/data.rs | 105 ++++++++ rog-slash/src/error.rs | 39 +++ rog-slash/src/lib.rs | 11 +- rog-slash/src/usb.rs | 404 +++++++--------------------- slashctl/Cargo.toml | 18 ++ slashctl/src/ctrl_slash/mod.rs | 94 +++++++ slashctl/src/error.rs | 30 +++ slashctl/src/main.rs | 15 ++ 33 files changed, 887 insertions(+), 367 deletions(-) create mode 100644 asusctl/src/slash_cli.rs create mode 100644 asusd/src/ctrl_slash/config.rs create mode 100644 asusd/src/ctrl_slash/mod.rs create mode 100644 asusd/src/ctrl_slash/trait_impls.rs create mode 100644 rog-dbus/src/zbus_slash.rs create mode 100644 rog-slash/src/data.rs create mode 100644 rog-slash/src/error.rs create mode 100644 slashctl/Cargo.toml create mode 100644 slashctl/src/ctrl_slash/mod.rs create mode 100644 slashctl/src/error.rs create mode 100644 slashctl/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index bdebc464..53cad718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,7 @@ dependencies = [ "rog_dbus", "rog_platform", "rog_profiles", + "rog_slash", "tinybmp", "tokio", "toml 0.5.11", @@ -160,6 +161,7 @@ dependencies = [ "rog_aura", "rog_platform", "rog_profiles", + "rog_slash", "serde", "serde_derive", "systemd-zbus", @@ -3500,6 +3502,7 @@ dependencies = [ "rog_aura", "rog_platform", "rog_profiles", + "rog_slash", "zbus 4.0.1", ] @@ -3545,6 +3548,23 @@ dependencies = [ "uhid-virt", ] +[[package]] +name = "rog_slash" +version = "6.0.0-alpha1" +dependencies = [ + "cargo-husky", + "dmi_id", + "gif 0.12.0", + "glam", + "log", + "pix", + "png_pong", + "serde", + "serde_derive", + "typeshare", + "zbus 4.0.1", +] + [[package]] name = "ron" version = "0.8.1" @@ -3883,6 +3903,19 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slashctl" +version = "6.0.0-alpha1" +dependencies = [ + "dmi_id", + "futures-lite 1.13.0", + "inotify", + "rog_platform", + "rog_profiles", + "rog_slash", + "udev 0.8.0", +] + [[package]] name = "slint" version = "1.5.1" diff --git a/Cargo.toml b/Cargo.toml index e72dafa6..683a12dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "asusctl", + "slashctl", "asusd", "asusd-user", "config-traits", @@ -12,10 +13,12 @@ members = [ "rog-aura", "rog-profiles", "rog-control-center", + "rog-slash", "simulators", ] default-members = [ "asusctl", + "slashctl", "asusd", "asusd-user", "cpuctl", diff --git a/Makefile b/Makefile index f5f7a967..5cc825c9 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ zshcpl = $(datarootdir)/zsh/site-functions BIN_ROG := rog-control-center BIN_C := asusctl +BIN_S := slashctl BIN_D := asusd BIN_U := asusd-user LEDCFG := aura_support.ron @@ -47,6 +48,7 @@ install-program: $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)" $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)" + $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_S)" "$(DESTDIR)$(bindir)/$(BIN_S)" $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)" $(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)" @@ -86,6 +88,7 @@ uninstall: rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png" rm -f "$(DESTDIR)$(bindir)/$(BIN_C)" + rm -f "$(DESTDIR)$(bindir)/$(BIN_S)" rm -f "$(DESTDIR)$(bindir)/$(BIN_D)" rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules" rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)" @@ -142,6 +145,7 @@ endif cargo build $(ARGS) ifneq ($(STRIP_BINARIES),0) strip -s ./target/$(TARGET)/$(BIN_C) + strip -s ./target/$(TARGET)/$(BIN_S) strip -s ./target/$(TARGET)/$(BIN_D) strip -s ./target/$(TARGET)/$(BIN_U) strip -s ./target/$(TARGET)/$(BIN_ROG) diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index ead75c5b..d905f92f 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -7,6 +7,7 @@ version.workspace = true [dependencies] rog_anime = { path = "../rog-anime" } +rog_slash = { path = "../rog-slash" } rog_aura = { path = "../rog-aura" } rog_dbus = { path = "../rog-dbus" } rog_profiles = { path = "../rog-profiles" } diff --git a/asusctl/src/aura_cli.rs b/asusctl/src/aura_cli.rs index 28cc3b89..b30cffb9 100644 --- a/asusctl/src/aura_cli.rs +++ b/asusctl/src/aura_cli.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::str::FromStr; use gumdrop::Options; @@ -87,15 +88,15 @@ impl FromStr for LedBrightness { } } } -impl ToString for LedBrightness { - fn to_string(&self) -> String { +impl Display for LedBrightness { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self.level { Some(0x00) => "low", Some(0x01) => "med", Some(0x02) => "high", _ => "unknown", }; - s.to_owned() + write!(f, "{}", s.to_owned()) } } diff --git a/asusctl/src/cli_opts.rs b/asusctl/src/cli_opts.rs index ba248371..3ff98aa2 100644 --- a/asusctl/src/cli_opts.rs +++ b/asusctl/src/cli_opts.rs @@ -4,6 +4,7 @@ use rog_platform::platform::ThrottlePolicy; use crate::anime_cli::AnimeCommand; use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin}; use crate::fan_curve_cli::FanCurveCommand; +use crate::slash_cli::SlashCommand; #[derive(Default, Options)] pub struct CliStart { @@ -41,6 +42,8 @@ pub enum CliCommand { Graphics(GraphicsCommand), #[options(name = "anime", help = "Manage AniMe Matrix")] Anime(AnimeCommand), + #[options(name = "slash", help = "Manage Slash Ledbar")] + Slash(SlashCommand), #[options(help = "Change bios settings")] Bios(BiosCommand), } diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index 2ed66005..0555325c 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -19,14 +19,17 @@ use rog_dbus::zbus_aura::AuraProxyBlocking; use rog_dbus::RogDbusClientBlocking; use rog_platform::platform::{GpuMode, Properties, ThrottlePolicy}; use rog_profiles::error::ProfileError; +use rog_slash::SlashMode; use crate::aura_cli::{AuraPowerStates, LedBrightness}; use crate::cli_opts::*; +use crate::slash_cli::{SlashCommand}; mod anime_cli; mod aura_cli; mod cli_opts; mod fan_curve_cli; +mod slash_cli; fn main() { let args: Vec = args().skip(1).collect(); @@ -149,6 +152,7 @@ fn do_parsed( } Some(CliCommand::Graphics(_)) => do_gfx(), Some(CliCommand::Anime(cmd)) => handle_anime(dbus, cmd)?, + Some(CliCommand::Slash(cmd)) => handle_slash(dbus, cmd)?, Some(CliCommand::Bios(cmd)) => handle_platform_properties(dbus, supported_properties, cmd)?, None => { if (!parsed.show_supported @@ -458,6 +462,49 @@ fn verify_brightness(brightness: f32) { } } +fn handle_slash( + dbus: &RogDbusClientBlocking<'_>, + cmd: &SlashCommand, +) -> Result<(), Box> { + if ( + cmd.enable_display.is_none() && + cmd.brightness.is_none() && + cmd.interval.is_none() && + !cmd.list && + !cmd.next && + !cmd.prev + ) || cmd.help + { + println!("Missing arg or command\n\n{}", cmd.self_usage()); + if let Some(lst) = cmd.self_command_list() { + println!("\n{}", lst); + } + } + if let Some(enable) = cmd.enable_display { + dbus.proxies().slash().set_enable_display(enable)?; + } + if let Some(brightness) = cmd.brightness { + dbus.proxies().slash().set_brightness(brightness)?; + } + if let Some(interval) = cmd.interval { + dbus.proxies().slash().set_interval(interval)?; + } + if cmd.next { + dbus.proxies().slash().set_current_mode(SlashMode::Bounce as u8)?; + } + if cmd.prev { + dbus.proxies().slash().set_current_mode(SlashMode::Flow as u8)?; + } + if cmd.list { + let res = SlashMode::list(); + for p in &res { + println!("{:?}", p); + } + } + + Ok(()) +} + fn handle_led_mode( aura: &AuraProxyBlocking, mode: &LedModeCommand, diff --git a/asusctl/src/slash_cli.rs b/asusctl/src/slash_cli.rs new file mode 100644 index 00000000..65ba1c45 --- /dev/null +++ b/asusctl/src/slash_cli.rs @@ -0,0 +1,19 @@ +use gumdrop::Options; + +#[derive(Options)] +pub struct SlashCommand { + #[options(help = "print help message")] + pub help: bool, + #[options(meta = "", help = "enable/disable the display")] + pub enable_display: Option, + #[options(meta = "", help = "set brightness value <0-255>")] + pub brightness: Option, + #[options(meta = "", help = "set interval value <0-255>")] + pub interval: Option, + #[options(help = "toggle to next animation in list")] + pub next: bool, + #[options(help = "toggle to previous animation in list")] + pub prev: bool, + #[options(help = "list available animations")] + pub list: bool, +} \ No newline at end of file diff --git a/asusd/Cargo.toml b/asusd/Cargo.toml index 2e5abfba..83f73571 100644 --- a/asusd/Cargo.toml +++ b/asusd/Cargo.toml @@ -16,6 +16,7 @@ path = "src/daemon.rs" [dependencies] config-traits = { path = "../config-traits" } rog_anime = { path = "../rog-anime", features = ["dbus"] } +rog_slash = { path = "../rog-slash", features = ["dbus"] } rog_aura = { path = "../rog-aura", features = ["dbus"] } rog_platform = { path = "../rog-platform" } rog_profiles = { path = "../rog-profiles" } diff --git a/asusd/src/config.rs b/asusd/src/config.rs index f3a2ea95..b42aa83c 100644 --- a/asusd/src/config.rs +++ b/asusd/src/config.rs @@ -90,13 +90,13 @@ impl StdConfig for Config { } } - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - fn file_name(&self) -> String { CONFIG_FILE.to_owned() } + + fn config_dir() -> std::path::PathBuf { + std::path::PathBuf::from(crate::CONFIG_PATH_BASE) + } } impl StdConfigLoad3 for Config {} diff --git a/asusd/src/ctrl_anime/config.rs b/asusd/src/ctrl_anime/config.rs index 507f1d21..2fe9a655 100644 --- a/asusd/src/ctrl_anime/config.rs +++ b/asusd/src/ctrl_anime/config.rs @@ -150,13 +150,13 @@ impl StdConfig for AnimeConfig { Self::create_default() } - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - fn file_name(&self) -> String { CONFIG_FILE.to_owned() } + + fn config_dir() -> std::path::PathBuf { + std::path::PathBuf::from(crate::CONFIG_PATH_BASE) + } } impl StdConfigLoad2 for AnimeConfig {} diff --git a/asusd/src/ctrl_aura/config.rs b/asusd/src/ctrl_aura/config.rs index 3119124a..d4729448 100644 --- a/asusd/src/ctrl_aura/config.rs +++ b/asusd/src/ctrl_aura/config.rs @@ -127,16 +127,16 @@ impl StdConfig for AuraConfig { panic!("This should not be used"); } - fn config_dir() -> std::path::PathBuf { - std::path::PathBuf::from(crate::CONFIG_PATH_BASE) - } - fn file_name(&self) -> String { if self.config_name.is_empty() { panic!("Config file name should not be empty"); } self.config_name.to_owned() } + + fn config_dir() -> std::path::PathBuf { + std::path::PathBuf::from(crate::CONFIG_PATH_BASE) + } } impl StdConfigLoad for AuraConfig {} diff --git a/asusd/src/ctrl_aura/controller.rs b/asusd/src/ctrl_aura/controller.rs index a914b924..cfce7100 100644 --- a/asusd/src/ctrl_aura/controller.rs +++ b/asusd/src/ctrl_aura/controller.rs @@ -348,7 +348,7 @@ mod tests { }; let mut controller = CtrlKbdLed { led_prod: AuraDevice::X19b6, - led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap()), + led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap().0), supported_data: supported_basic_modes, per_key_mode_active: false, config, @@ -386,7 +386,7 @@ mod tests { }; let mut controller = CtrlKbdLed { led_prod: AuraDevice::X19b6, - led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap()), + led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap().0), supported_data: supported_basic_modes, per_key_mode_active: false, config, diff --git a/asusd/src/ctrl_slash/config.rs b/asusd/src/ctrl_slash/config.rs new file mode 100644 index 00000000..8ea0568f --- /dev/null +++ b/asusd/src/ctrl_slash/config.rs @@ -0,0 +1,52 @@ +use serde_derive::{Deserialize, Serialize}; +use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2}; +use rog_slash::{DeviceState, SlashMode}; +use crate::ctrl_anime::config::{AnimeConfig, AnimeConfigV460, AnimeConfigV472}; + +const CONFIG_FILE: &str = "slash.ron"; + +/// Config for base system actions for the anime display +#[derive(Deserialize, Serialize, Debug)] +pub struct SlashConfig { + pub slash_enabled: bool, + pub slash_brightness: u8, + pub slash_interval: u8, + pub slash_mode: u8, +} + +impl Default for SlashConfig { + fn default() -> Self { + SlashConfig { + slash_enabled: true, + slash_brightness: 255, + slash_interval: 0, + slash_mode: SlashMode::Bounce as u8, + } + } +} +impl StdConfig for SlashConfig { + fn new() -> Self { + Self::default() + } + + fn file_name(&self) -> String { + CONFIG_FILE.to_owned() + } + + fn config_dir() -> std::path::PathBuf { + std::path::PathBuf::from(crate::CONFIG_PATH_BASE) + } +} + +impl StdConfigLoad for SlashConfig {} + +impl From<&SlashConfig> for DeviceState { + fn from(config: &SlashConfig) -> Self { + DeviceState { + slash_enabled: config.slash_enabled, + slash_brightness: config.slash_brightness, + slash_interval: config.slash_interval, + slash_mode: config.slash_mode, + } + } +} \ No newline at end of file diff --git a/asusd/src/ctrl_slash/mod.rs b/asusd/src/ctrl_slash/mod.rs new file mode 100644 index 00000000..c571468e --- /dev/null +++ b/asusd/src/ctrl_slash/mod.rs @@ -0,0 +1,100 @@ +pub mod config; +pub mod trait_impls; + +use rog_platform::hid_raw::HidRaw; +use rog_platform::usb_raw::USBRaw; +use rog_slash::{SlashMode}; +use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init}; +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 fn set_builtins_enabled(&self, enabled: bool) -> Result<(), SlashCtlError> { + // 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)) + // Ok(()) + // } +} + +#[derive(Clone)] +pub struct CtrlSlash { + // node: HidRaw, + node: Node, + config: SlashConfig, + // slash_type: SlashType, + // // set to force thread to exit + // thread_exit: Arc, + // // Set to false when the thread exits + // thread_running: Arc, +} + +impl CtrlSlash { + #[inline] + pub fn new(config: SlashConfig) -> Result { + 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() { + unsafe { Node::Usb(usb.unwrap_unchecked()) } + } else if hid.is_some() { + unsafe { Node::Hid(hid.unwrap_unchecked().0) } + } else { + return Err(RogError::NotSupported); + }; + + // Maybe, detecting the slash-type may become necessary + // let slash_type = get_slash_type()?; + + let ctrl = CtrlSlash { + node, + config, + // slash_type, + // thread_exit: Arc::new(AtomicBool::new(false)), + // thread_running: Arc::new(AtomicBool::new(false)), + }; + 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])?; + + 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 as u8); + self.node.write_bytes(&command_packets[0])?; + self.node.write_bytes(&command_packets[1])?; + Ok(()) + } +} \ No newline at end of file diff --git a/asusd/src/ctrl_slash/trait_impls.rs b/asusd/src/ctrl_slash/trait_impls.rs new file mode 100644 index 00000000..39e7afec --- /dev/null +++ b/asusd/src/ctrl_slash/trait_impls.rs @@ -0,0 +1,94 @@ +use std::sync::Arc; +use log::warn; +use zbus::{Connection, interface}; +use zbus::export::futures_util::lock::Mutex; +use config_traits::StdConfig; +use rog_slash::DeviceState; +use rog_slash::usb::pkt_set_options; +use crate::ctrl_slash::CtrlSlash; + + +pub const SLASH_ZBUS_NAME: &str = "Slash"; +pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux"; + +#[derive(Clone)] +pub struct CtrlSlashZbus(pub Arc>); + + +/// 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_NAME, server).await; + } +} + + +// None of these calls can be guarnateed to succeed unless we loop until okay +// If the try_lock *does* succeed then any other thread trying to lock will not +// grab it until we finish. +#[interface(name = "org.asuslinux.Slash")] +impl CtrlSlashZbus { + // /// Writes a data stream of length. Will force system thread to exit until + // /// it is restarted + // async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { + // let lock = self.0.lock().await; + // lock.thread_exit.store(true, Ordering::SeqCst); + // lock.write_data_buffer(input).map_err(|err| { + // warn!("ctrl_anime::run_animation:callback {}", err); + // err + // })?; + // Ok(()) + // } + + /// Set base brightness level + #[zbus(property)] + async fn brightness(&self) -> u8 { + let lock = self.0.lock().await; + lock.config.slash_brightness + } + + /// Set base 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 enable_display(&self) -> bool { + let lock = self.0.lock().await; + lock.config.slash_enabled + } + + /// Set whether the AniMe is enabled at all + #[zbus(property)] + async fn set_enable_display(&self, enabled: bool) { + let mut lock = self.0.lock().await; + lock.node + .write_bytes(&pkt_set_options(enabled, lock.config.slash_brightness, lock.config.slash_interval)) + .map_err(|err| { + warn!("ctrl_slash::set_options {}", err); + }) + .ok(); + + lock.config.slash_enabled = enabled; + 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) + } +} \ No newline at end of file diff --git a/asusd/src/daemon.rs b/asusd/src/daemon.rs index e6d09984..38d485ef 100644 --- a/asusd/src/daemon.rs +++ b/asusd/src/daemon.rs @@ -12,9 +12,12 @@ use asusd::ctrl_aura::manager::AuraManager; use asusd::ctrl_fancurves::CtrlFanCurveZbus; use asusd::ctrl_platform::CtrlPlatform; use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME}; -use config_traits::{StdConfig, StdConfigLoad2, StdConfigLoad3}; +use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2, StdConfigLoad3}; use log::{error, info}; use zbus::fdo::ObjectManager; +use asusd::ctrl_slash::config::SlashConfig; +use asusd::ctrl_slash::CtrlSlash; +use asusd::ctrl_slash::trait_impls::CtrlSlashZbus; #[tokio::main] async fn main() -> Result<(), Box> { @@ -41,6 +44,7 @@ async fn main() -> Result<(), Box> { info!(" daemon v{}", asusd::VERSION); info!(" rog-anime v{}", rog_anime::VERSION); + info!(" rog-slash v{}", rog_slash::VERSION); info!(" rog-aura v{}", rog_aura::VERSION); info!(" rog-profiles v{}", rog_profiles::VERSION); info!("rog-platform v{}", rog_platform::VERSION); @@ -104,6 +108,16 @@ async fn start_daemon() -> Result<(), Box> { } } + match CtrlSlash::new(SlashConfig::new().load()) { + Ok(ctrl) => { + let sig_ctx = CtrlPlatform::signal_context(&connection)?; + start_tasks(ctrl, &mut connection, sig_ctx).await?; + } + Err(err) => { + info!("Slash control: {}", err); + } + } + let _ = AuraManager::new(connection.clone()).await?; // Request dbus name after finishing initalizing all functions diff --git a/asusd/src/lib.rs b/asusd/src/lib.rs index aaeff5fc..16f21e6f 100644 --- a/asusd/src/lib.rs +++ b/asusd/src/lib.rs @@ -3,6 +3,8 @@ pub mod config; /// Control of anime matrix display pub mod ctrl_anime; +/// Control of anime slash led bar +pub mod ctrl_slash; /// Keyboard LED brightness control, RGB, and LED display modes pub mod ctrl_aura; /// Control platform profiles + fan-curves if available diff --git a/rog-control-center/src/config.rs b/rog-control-center/src/config.rs index c8a52d47..7a39b40b 100644 --- a/rog-control-center/src/config.rs +++ b/rog-control-center/src/config.rs @@ -50,6 +50,10 @@ impl StdConfig for Config { } } + fn file_name(&self) -> String { + CFG_FILE_NAME.to_owned() + } + fn config_dir() -> std::path::PathBuf { let mut path = dirs::config_dir().unwrap_or_default(); @@ -62,10 +66,6 @@ impl StdConfig for Config { } path } - - fn file_name(&self) -> String { - CFG_FILE_NAME.to_owned() - } } impl StdConfigLoad1 for Config {} diff --git a/rog-control-center/src/main.rs b/rog-control-center/src/main.rs index fbda2320..325c378b 100644 --- a/rog-control-center/src/main.rs +++ b/rog-control-center/src/main.rs @@ -35,14 +35,10 @@ fn main() -> Result<()> { let is_rog_ally = prod_family == "RC71L"; // tmp-dir must live to the end of program life - let _tmp_dir = match tempfile::Builder::new() + let _tmp_dir = tempfile::Builder::new() .prefix("rog-gui") .rand_bytes(0) - .tempdir() - { - Ok(tmp) => tmp, - Err(_) => on_tmp_dir_exists().unwrap(), - }; + .tempdir().unwrap_or_else(|_| on_tmp_dir_exists().unwrap()); let args: Vec = args().skip(1).collect(); @@ -76,13 +72,10 @@ fn main() -> Result<()> { }) .unwrap(); - let supported_properties = match dbus.proxies().platform().supported_properties() { - Ok(s) => s, - Err(_e) => { - // TODO: show an error window - Vec::default() - } - }; + let supported_properties = dbus.proxies().platform().supported_properties().unwrap_or_else(|_e| { + // TODO: show an error window + Vec::default() + }); // Startup let mut config = Config::new().load(); diff --git a/rog-dbus/Cargo.toml b/rog-dbus/Cargo.toml index d366edb1..d6f74c7f 100644 --- a/rog-dbus/Cargo.toml +++ b/rog-dbus/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" [dependencies] asusd = { path = "../asusd" } rog_anime = { path = "../rog-anime", features = ["dbus"] } +rog_slash = { path = "../rog-slash", features = ["dbus"] } rog_aura = { path = "../rog-aura" } rog_profiles = { path = "../rog-profiles" } rog_platform = { path = "../rog-platform" } diff --git a/rog-dbus/src/lib.rs b/rog-dbus/src/lib.rs index 0059de43..13e6ccd9 100644 --- a/rog-dbus/src/lib.rs +++ b/rog-dbus/src/lib.rs @@ -1,8 +1,10 @@ pub use asusd::{DBUS_IFACE, DBUS_NAME, DBUS_PATH}; + pub mod zbus_anime; pub mod zbus_aura; pub mod zbus_fan_curves; pub mod zbus_platform; +pub mod zbus_slash; // use rog_anime::AnimePowerStates; // use rog_aura::{AuraEffect, LedPowerStates}; @@ -14,6 +16,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct DbusProxiesBlocking<'a> { anime: zbus_anime::AnimeProxyBlocking<'a>, + slash: zbus_slash::SlashProxyBlocking<'a>, led: zbus_aura::AuraProxyBlocking<'a>, profile: zbus_fan_curves::FanCurvesProxyBlocking<'a>, rog_bios: zbus_platform::PlatformProxyBlocking<'a>, @@ -27,6 +30,7 @@ impl<'a> DbusProxiesBlocking<'a> { Ok(( DbusProxiesBlocking { anime: zbus_anime::AnimeProxyBlocking::new(&conn)?, + slash: zbus_slash::SlashProxyBlocking::new(&conn)?, led: zbus_aura::AuraProxyBlocking::new(&conn)?, profile: zbus_fan_curves::FanCurvesProxyBlocking::new(&conn)?, rog_bios: zbus_platform::PlatformProxyBlocking::new(&conn)?, @@ -38,15 +42,13 @@ impl<'a> DbusProxiesBlocking<'a> { pub fn anime(&self) -> &zbus_anime::AnimeProxyBlocking<'a> { &self.anime } - + pub fn slash(&self) -> &zbus_slash::SlashProxyBlocking<'a> { &self.slash } pub fn aura(&self) -> &zbus_aura::AuraProxyBlocking<'a> { &self.led } - pub fn fan_curves(&self) -> &zbus_fan_curves::FanCurvesProxyBlocking<'a> { &self.profile } - pub fn platform(&self) -> &zbus_platform::PlatformProxyBlocking<'a> { &self.rog_bios } @@ -71,6 +73,7 @@ impl<'a> RogDbusClientBlocking<'a> { pub struct DbusProxies<'a> { anime: zbus_anime::AnimeProxy<'a>, + slash: zbus_slash::SlashProxy<'a>, led: zbus_aura::AuraProxy<'a>, profile: zbus_fan_curves::FanCurvesProxy<'a>, rog_bios: zbus_platform::PlatformProxy<'a>, @@ -84,6 +87,7 @@ impl<'a> DbusProxies<'a> { Ok(( DbusProxies { anime: zbus_anime::AnimeProxy::new(&conn).await?, + slash: zbus_slash::SlashProxy::new(&conn).await?, led: zbus_aura::AuraProxy::new(&conn).await?, profile: zbus_fan_curves::FanCurvesProxy::new(&conn).await?, rog_bios: zbus_platform::PlatformProxy::new(&conn).await?, @@ -95,6 +99,7 @@ impl<'a> DbusProxies<'a> { pub fn anime(&self) -> &zbus_anime::AnimeProxy<'a> { &self.anime } + pub fn slash(&self) -> &zbus_slash::SlashProxy<'a> { &self.slash } pub fn led(&self) -> &zbus_aura::AuraProxy<'a> { &self.led diff --git a/rog-dbus/src/zbus_slash.rs b/rog-dbus/src/zbus_slash.rs new file mode 100644 index 00000000..f0104a29 --- /dev/null +++ b/rog-dbus/src/zbus_slash.rs @@ -0,0 +1,35 @@ +use zbus::proxy; + +#[proxy( +interface = "org.asuslinux.Slash", +default_service = "org.asuslinux.Daemon", +default_path = "/org/asuslinux" +)] +trait Slash { + /// RunMainLoop method + fn run_main_loop(&self, start: bool) -> zbus::Result<()>; + + /// Brightness property + #[zbus(property)] + fn brightness(&self) -> zbus::Result; + #[zbus(property)] + fn set_brightness(&self, value: u8) -> zbus::Result<()>; + + /// Interval property + #[zbus(property)] + fn interval(&self) -> zbus::Result; + #[zbus(property)] + fn set_interval(&self, value: u8) -> zbus::Result<()>; + + /// BuiltinAnimations property + #[zbus(property)] + fn current_mode(&self) -> zbus::Result; + #[zbus(property)] + fn set_current_mode(&self, value: u8) -> zbus::Result<()>; + + /// EnableDisplay property + #[zbus(property)] + fn enable_display(&self) -> zbus::Result; + #[zbus(property)] + fn set_enable_display(&self, value: bool) -> zbus::Result<()>; +} diff --git a/rog-platform/src/usb_raw.rs b/rog-platform/src/usb_raw.rs index 9670959d..9b853395 100644 --- a/rog-platform/src/usb_raw.rs +++ b/rog-platform/src/usb_raw.rs @@ -11,9 +11,11 @@ impl USBRaw { pub fn new(id_product: u16) -> Result { for device in rusb::devices()?.iter() { let device_desc = device.device_descriptor()?; - if device_desc.vendor_id() == 0x0b05 && device_desc.product_id() == id_product { - let handle = Self::get_dev_handle(&device)?; - return Ok(Self(handle)); + if device_desc.vendor_id() == 0x0b05 { + if device_desc.product_id() == id_product { + let handle = Self::get_dev_handle(&device)?; + return Ok(Self(handle)); + } } } @@ -27,11 +29,16 @@ impl USBRaw { device: &Device, ) -> Result> { // We don't expect this ID to ever change - let mut device = device.open()?; - device.reset()?; - device.set_auto_detach_kernel_driver(true)?; - device.claim_interface(0)?; - Ok(device) + let device_open = device.open(); + if let Err(err) = device_open { + panic!("Could not open device, try running as root: {}", err); + } else { + let mut device = device_open.unwrap(); + device.reset()?; + device.set_auto_detach_kernel_driver(true)?; + device.claim_interface(0)?; + Ok(device) + } } pub fn write_bytes(&self, message: &[u8]) -> Result { diff --git a/rog-slash/Cargo.toml b/rog-slash/Cargo.toml index 4f5864a2..4c2a21b8 100644 --- a/rog-slash/Cargo.toml +++ b/rog-slash/Cargo.toml @@ -2,28 +2,39 @@ name = "rog_slash" license = "MPL-2.0" version.workspace = true -readme = "README.md" authors = ["Luke "] repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl" -documentation = "https://docs.rs/rog-anime" +documentation = "https://docs.rs/rog-slash" description = "ASUS Slash display" keywords = ["ROG", "ASUS", "AniMe", "Slash"] -edition = "2024" +edition = "2021" exclude = ["data"] +[features] +default = ["dbus", "detect"] +dbus = ["zbus"] +detect = ["dmi_id"] + +[lib] +name = "rog_slash" +path = "src/lib.rs" + [dependencies] +png_pong.workspace = true +pix.workspace = true +gif.workspace = true log.workspace = true + serde.workspace = true serde_derive.workspace = true -zbus.workspace = true -concat-idents.workspace = true -udev.workspace = true -inotify.workspace = true + +glam.workspace = true typeshare.workspace = true -rusb.workspace = true +zbus = { workspace = true, optional = true } + +dmi_id = { path = "../dmi-id", optional = true } [dev-dependencies] -cargo-husky.workspace = true -rog_aura = { path = "../rog-aura" } \ No newline at end of file +cargo-husky.workspace = true \ No newline at end of file diff --git a/rog-slash/src/data.rs b/rog-slash/src/data.rs new file mode 100644 index 00000000..19e64da5 --- /dev/null +++ b/rog-slash/src/data.rs @@ -0,0 +1,105 @@ +use std::str::FromStr; + +use serde_derive::{Deserialize, Serialize}; +use typeshare::typeshare; +#[cfg(feature = "dbus")] +use zbus::zvariant::Type; + +use crate::error::SlashError; + +#[typeshare] +#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub enum SlashType { + GA403, + Unknown, +} + +impl FromStr for SlashType { + type Err = SlashError; + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "ga403" | "GA403" => Self::GA403, + _ => Self::Unknown, + }) + } +} + +#[typeshare] +// #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))] +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum SlashMode { + Bounce = 0x10, + Slash = 0x12, + Loading = 0x13, + BitStream = 0x1D, + Transmission = 0x1A, + Flow = 0x19, + Flux = 0x25, + Phantom = 0x24, + Spectrum = 0x26, + Hazard = 0x32, + Interfacing = 0x33, + Ramp = 0x34, + GameOver = 0x42, + Start = 0x43, + Buzzer = 0x44, +} + +impl SlashMode { + pub const fn to_string(&self) -> &str + { + match &self { + SlashMode::Bounce => "Bounce", + SlashMode::Slash => "Slash", + SlashMode::Loading => "Loading", + SlashMode::BitStream => "BitStream", + SlashMode::Transmission => "Transmission", + SlashMode::Flow => "Flow", + SlashMode::Flux => "Flux", + SlashMode::Phantom => "Phantom", + SlashMode::Spectrum => "Spectrum", + SlashMode::Hazard => "Hazard", + SlashMode::Interfacing => "Interfacing", + SlashMode::Ramp => "Ramp", + SlashMode::GameOver => "GameOver", + SlashMode::Start => "Start", + SlashMode::Buzzer => "Buzzer", + } + } + + pub const fn list() -> [&'static str; 15] { + [ + SlashMode::Bounce.to_string(), + SlashMode::Slash.to_string(), + SlashMode::Loading.to_string(), + SlashMode::BitStream.to_string(), + SlashMode::Transmission.to_string(), + SlashMode::Flow.to_string(), + SlashMode::Flux.to_string(), + SlashMode::Phantom.to_string(), + SlashMode::Spectrum.to_string(), + SlashMode::Hazard.to_string(), + SlashMode::Interfacing.to_string(), + SlashMode::Ramp.to_string(), + SlashMode::GameOver.to_string(), + SlashMode::Start.to_string(), + SlashMode::Buzzer.to_string(), + ] + } +} + + + +// TODO: move this out +#[typeshare] +#[cfg_attr(feature = "dbus", derive(Type))] +#[typeshare] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] +pub struct DeviceState { + pub slash_enabled: bool, + pub slash_brightness: u8, + pub slash_interval: u8, + pub slash_mode: u8, +} + diff --git a/rog-slash/src/error.rs b/rog-slash/src/error.rs new file mode 100644 index 00000000..af2e0fb6 --- /dev/null +++ b/rog-slash/src/error.rs @@ -0,0 +1,39 @@ +use std::error::Error; +use std::fmt; +pub type Result = std::result::Result; + +#[derive(Debug)] +pub enum SlashError { + Dbus(String), + Udev(String, std::io::Error), + NoDevice, + UnsupportedDevice, + DataBufferLength, + ParseError(String), +} + +impl fmt::Display for SlashError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SlashError::ParseError(e) => write!(f, "Could not parse {e}"), + SlashError::Dbus(detail) => write!(f, "{}", detail), + SlashError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error), + SlashError::NoDevice => write!(f, "No Slash device found"), + SlashError::DataBufferLength => write!( + f, + "The data buffer was incorrect length for generating USB packets" + ), + SlashError::UnsupportedDevice => write!(f, "Unsupported Slash device found"), + } + } +} + +impl Error for SlashError {} + +impl From for zbus::fdo::Error { + #[inline] + fn from(err: SlashError) -> Self { + zbus::fdo::Error::Failed(format!("{}", err)) + } +} diff --git a/rog-slash/src/lib.rs b/rog-slash/src/lib.rs index ffb4a0c5..3a50da90 100644 --- a/rog-slash/src/lib.rs +++ b/rog-slash/src/lib.rs @@ -1,3 +1,12 @@ +/// The main data conversion for transfering in shortform over dbus or other, +/// or writing directly to the USB device +mod data; +pub use data::*; + +/// Base errors that are possible +pub mod error; /// Provides const methods to create the USB HID control packets -pub mod usb; \ No newline at end of file +pub mod usb; + +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); \ No newline at end of file diff --git a/rog-slash/src/usb.rs b/rog-slash/src/usb.rs index d0dcc9ca..38aabc49 100644 --- a/rog-slash/src/usb.rs +++ b/rog-slash/src/usb.rs @@ -1,345 +1,129 @@ -//! Utils for writing to the `AniMe` USB device +//! Utils for writing to the `Slash` USB device //! //! Use of the device requires a few steps: //! 1. Initialise the device by writing the two packets from -//! `get_init_packets()` 2. Write data from `AnimePacketType` +//! `get_init_packets()` 2. Write data from `SLashPacketType` //! 3. Write the packet from `get_flush_packet()`, which tells the device to //! display the data from step 2 //! -//! Step 1 need to applied only on fresh system boot. - -use std::str::FromStr; +//! Step 1 needs to be applied only on fresh system boot. use dmi_id::DMIID; -use serde_derive::{Deserialize, Serialize}; -use typeshare::typeshare; #[cfg(feature = "dbus")] -use zbus::zvariant::{OwnedValue, Type, Value}; +use crate::error::SlashError; +use crate::SlashType; -use crate::error::AnimeError; -use crate::AnimeType; - -const PACKET_SIZE: usize = 640; +const PACKET_SIZE: usize = 128; const DEV_PAGE: u8 = 0x5e; -pub const VENDOR_ID: u16 = 0x0b05; -pub const PROD_ID: u16 = 0x193b; +pub const VENDOR_ID: u16 = 0x0B05; +pub const PROD_ID: u16 = 0x193B; +pub const PROD_ID_STR: &str = "193B"; -#[cfg_attr( -feature = "dbus", -derive(Type, Value, OwnedValue), -zvariant(signature = "u") -)] -#[typeshare] -#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -/// Base LED brightness of the display -pub enum Brightness { - Off = 0, - Low = 1, - #[default] - Med = 2, - High = 3, -} +pub type SlashUsbPacket = [u8;PACKET_SIZE]; -impl FromStr for Brightness { - type Err = AnimeError; - - fn from_str(s: &str) -> Result { - Ok(match s { - "Off" | "off" => Brightness::Off, - "Low" | "low" => Brightness::Low, - "Med" | "med" => Brightness::Med, - "High" | "high" => Brightness::High, - _ => Brightness::Med, - }) - } -} - -impl From for Brightness { - fn from(v: u8) -> Brightness { - match v { - 0 => Brightness::Off, - 1 => Brightness::Low, - 3 => Brightness::High, - _ => Brightness::Med, - } - } -} - -impl From for Brightness { - fn from(v: i32) -> Brightness { - (v as u8).into() - } -} - -impl From for i32 { - fn from(v: Brightness) -> i32 { - v as i32 - } -} - -#[cfg_attr( -feature = "dbus", -derive(Type, Value, OwnedValue), -zvariant(signature = "s") -)] -#[typeshare] -#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -pub enum AnimBooting { - #[default] - GlitchConstruction = 0, - StaticEmergence = 1, -} - -impl FromStr for AnimBooting { - type Err = AnimeError; - - fn from_str(s: &str) -> Result { - match s { - "GlitchConstruction" => Ok(Self::GlitchConstruction), - "StaticEmergence" => Ok(Self::StaticEmergence), - _ => Err(AnimeError::ParseError(s.to_owned())), - } - } -} - -impl From for AnimBooting { - fn from(value: i32) -> Self { - match value { - 0 => Self::GlitchConstruction, - 1 => Self::StaticEmergence, - _ => Self::default(), - } - } -} - -impl From for i32 { - fn from(value: AnimBooting) -> Self { - value as i32 - } -} - -#[cfg_attr( -feature = "dbus", -derive(Type, Value, OwnedValue), -zvariant(signature = "s") -)] -#[typeshare] -#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -pub enum AnimAwake { - #[default] - BinaryBannerScroll = 0, - RogLogoGlitch = 1, -} - -impl FromStr for AnimAwake { - type Err = AnimeError; - - fn from_str(s: &str) -> Result { - match s { - "BinaryBannerScroll" => Ok(Self::BinaryBannerScroll), - "RogLogoGlitch" => Ok(Self::RogLogoGlitch), - _ => Err(AnimeError::ParseError(s.to_owned())), - } - } -} - -impl From for AnimAwake { - fn from(value: i32) -> Self { - match value { - 0 => Self::BinaryBannerScroll, - 1 => Self::RogLogoGlitch, - _ => Self::default(), - } - } -} - -impl From for i32 { - fn from(value: AnimAwake) -> Self { - value as i32 - } -} - -#[cfg_attr( -feature = "dbus", -derive(Type, Value, OwnedValue), -zvariant(signature = "s") -)] -#[typeshare] -#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -pub enum AnimSleeping { - #[default] - BannerSwipe = 0, - Starfield = 1, -} - -impl FromStr for AnimSleeping { - type Err = AnimeError; - - fn from_str(s: &str) -> Result { - match s { - "BannerSwipe" => Ok(Self::BannerSwipe), - "Starfield" => Ok(Self::Starfield), - _ => Err(AnimeError::ParseError(s.to_owned())), - } - } -} - -impl From for AnimSleeping { - fn from(value: i32) -> Self { - match value { - 0 => Self::BannerSwipe, - 1 => Self::Starfield, - _ => Self::default(), - } - } -} - -impl From for i32 { - fn from(value: AnimSleeping) -> Self { - value as i32 - } -} - -#[cfg_attr( -feature = "dbus", -derive(Type, Value, OwnedValue), -zvariant(signature = "s") -)] -#[typeshare] -#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)] -pub enum AnimShutdown { - #[default] - GlitchOut = 0, - SeeYa = 1, -} - -impl FromStr for AnimShutdown { - type Err = AnimeError; - - fn from_str(s: &str) -> Result { - match s { - "GlitchOut" => Ok(Self::GlitchOut), - "SeeYa" => Ok(Self::SeeYa), - _ => Err(AnimeError::ParseError(s.to_owned())), - } - } -} - -impl From for AnimShutdown { - fn from(value: i32) -> Self { - match value { - 0 => Self::GlitchOut, - 1 => Self::SeeYa, - _ => Self::default(), - } - } -} - -impl From for i32 { - fn from(value: AnimShutdown) -> Self { - value as i32 - } -} /// `get_anime_type` is very broad, matching on part of the laptop board name /// only. For this reason `find_node()` must be used also to verify if the USB /// device is available. /// -/// The currently known USB device is `19b6`. +/// The currently known USB device is `193B`. #[inline] -pub fn get_anime_type() -> Result { - let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error +pub fn get_slash_type() -> Result { + let dmi = DMIID::new().map_err(|_| SlashError::NoDevice)?; // TODO: better error let board_name = dmi.board_name; - if board_name.contains("GA401I") || board_name.contains("GA401Q") { - return Ok(AnimeType::GA401); - } else if board_name.contains("GA402R") { - return Ok(AnimeType::GA402); - } else if board_name.contains("GU604V") { - return Ok(AnimeType::GU604); + if board_name.contains("GA403") { + return Ok(SlashType::GA403); } - log::warn!("AniMe Matrix device found but not yet supported, will default to a GA402 layout"); - Ok(AnimeType::Unknown) + log::warn!("AniMe Slash device found but not yet supported, will default to a GA403 layout"); + Ok(SlashType::Unknown) } /// Get the two device initialization packets. These are required for device /// start after the laptop boots. #[inline] -pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] { - let mut packets = [[0; PACKET_SIZE]; 2]; - packets[0][0] = DEV_PAGE; // This is the USB page we're using throughout - let mut count = 0; - // TODO: memcpy or slice copy - let bytes = "ASUS Tech.Inc.".as_bytes(); - while count < bytes.len() { - packets[0][count + 1] = bytes[count]; - count += 1; - } - // - packets[1][0] = DEV_PAGE; - packets[1][1] = 0xc2; - packets +pub const fn pkts_for_init() -> [SlashUsbPacket; 2] { + let mut pkt1 = [0;PACKET_SIZE]; + pkt1[0] = DEV_PAGE; + pkt1[1] = 0xD7; + pkt1[2] = 0x00; + pkt1[3] = 0x00; + pkt1[4] = 0x01; + pkt1[5] = 0xAC; + + let mut pkt2 = [0;PACKET_SIZE]; + pkt2[0] = DEV_PAGE; + pkt2[1] = 0xD2; + pkt2[2] = 0x02; + pkt2[3] = 0x01; + pkt2[4] = 0x08; + pkt2[5] = 0xAB; + + [pkt1, pkt2] } -/// Should be written to the device after writing the two main data packets that -/// make up the display data packet #[inline] -pub const fn pkt_flush() -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; +pub const fn pkt_save() -> SlashUsbPacket { + let mut pkt = [0;PACKET_SIZE]; pkt[0] = DEV_PAGE; - pkt[1] = 0xc0; + pkt[1] = 0xD4; + pkt[2] = 0x00; + pkt[3] = 0x00; + pkt[4] = 0x01; + pkt[5] = 0xAB; + + pkt +} + +#[inline] +pub const fn pkt_set_mode(mode: u8) -> [SlashUsbPacket; 2] { + let mut pkt1 = [0;PACKET_SIZE]; + pkt1[0] = DEV_PAGE; + pkt1[1] = 0x02; + pkt1[2] = 0x03; + pkt1[3] = 0x00; + pkt1[4] = 0x0C; + + let mut pkt2 = [0;PACKET_SIZE]; + pkt2[0] = DEV_PAGE; + pkt2[1] = 0xD3; + pkt2[2] = 0x04; + pkt2[3] = 0x00; + pkt2[4] = 0x0C; + pkt2[5] = 0x01; + pkt2[6] = mode; + pkt2[7] = 0x02; + pkt2[8] = 0x19; + pkt2[9] = 0x03; + pkt2[10] = 0x13; + pkt2[11] = 0x04; + pkt2[12] = 0x11; + pkt2[13] = 0x05; + pkt2[14] = 0x12; + pkt2[15] = 0x06; + pkt2[16] = 0x13; + + [pkt1, pkt2] +} + +#[inline] +pub const fn pkt_set_options(enabled: bool, brightness: u8, interval: u8) -> SlashUsbPacket { + let status_byte = if enabled { 0x01 } else { 0x00 }; + + let mut pkt = [0;PACKET_SIZE]; + pkt[0] = DEV_PAGE; + pkt[1] = 0xD3; pkt[2] = 0x03; - pkt -} + pkt[3] = 0x01; + pkt[4] = 0x08; + pkt[5] = 0xAB; + pkt[6] = 0xFF; + pkt[7] = 0x01; + pkt[8] = status_byte; + pkt[9] = 0x06; + pkt[10] = brightness; + pkt[11] = 0xFF; + pkt[12] = interval; -/// Packet for setting the brightness (0-3). Requires -/// `pkt_for_apply()` to be written after. -#[inline] -pub const fn pkt_set_brightness(brightness: Brightness) -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; - pkt[0] = DEV_PAGE; - pkt[1] = 0xc0; - pkt[2] = 0x04; - pkt[3] = brightness as u8; - pkt -} - -/// Enable the display? -#[inline] -pub const fn pkt_set_enable_display(status: bool) -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; - pkt[0] = DEV_PAGE; - pkt[1] = 0xc3; - pkt[2] = 0x01; - pkt[3] = if status { 0x00 } else { 0x80 }; - pkt -} - -/// Enable builtin animations? -#[inline] -pub const fn pkt_set_enable_powersave_anim(status: bool) -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; - pkt[0] = DEV_PAGE; - pkt[1] = 0xc4; - pkt[2] = 0x01; - pkt[3] = if status { 0x00 } else { 0x80 }; - pkt -} - -/// Set which animations are shown for each stage -#[inline] -pub const fn pkt_set_builtin_animations( - boot: AnimBooting, - awake: AnimAwake, - sleep: AnimSleeping, - shutdown: AnimShutdown, -) -> [u8; PACKET_SIZE] { - let mut pkt = [0; PACKET_SIZE]; - pkt[0] = DEV_PAGE; - pkt[1] = 0xc5; - pkt[2] = (awake as u8) - | ((sleep as u8) << 0x01) - | ((shutdown as u8) << 0x02) - | ((boot as u8) << 0x03); pkt } diff --git a/slashctl/Cargo.toml b/slashctl/Cargo.toml new file mode 100644 index 00000000..fb85f31b --- /dev/null +++ b/slashctl/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "slashctl" +license = "MPL-2.0" +version.workspace = true +authors = ["Luke "] +repository = "https://gitlab.com/asus-linux/asus-nb-ctrl" +homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl" +description = "A ctrl app to control the Asus Slash" +edition = "2021" + +[dependencies] +rog_slash = { path = "../rog-slash" } +rog_platform = { path = "../rog-platform" } +rog_profiles = { path = "../rog-profiles" } +dmi_id = { path = "../dmi-id" } +futures-lite = "*" +udev.workspace = true +inotify.workspace = true \ No newline at end of file diff --git a/slashctl/src/ctrl_slash/mod.rs b/slashctl/src/ctrl_slash/mod.rs new file mode 100644 index 00000000..00c1db97 --- /dev/null +++ b/slashctl/src/ctrl_slash/mod.rs @@ -0,0 +1,94 @@ +use rog_platform::hid_raw::HidRaw; +use rog_platform::usb_raw::USBRaw; +use rog_slash::{SlashMode}; +use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init}; + +use crate::error::SlashCtlError; + +enum Node { + Usb(USBRaw), + Hid(HidRaw), +} + +impl Node { + pub fn write_bytes(&self, message: &[u8]) -> Result<(), SlashCtlError> { + // 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) -> Result<(), SlashCtlError> { + // 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)) + // Ok(()) + // } +} + +pub struct CtrlSlash { + // node: HidRaw, + node: Node, + // slash_type: SlashType, + // // set to force thread to exit + // thread_exit: Arc, + // // Set to false when the thread exits + // thread_running: Arc, +} + +impl CtrlSlash { + #[inline] + pub fn new() -> Result { + 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() { + unsafe { Node::Usb(usb.unwrap_unchecked()) } + } else if hid.is_some() { + unsafe { Node::Hid(hid.unwrap_unchecked().0) } + } else { + return Err(SlashCtlError::NotSupported); + }; + + // Maybe, detecting the slash-type may become necessary + // let slash_type = get_slash_type()?; + + let ctrl = CtrlSlash { + node, + // slash_type, + // thread_exit: Arc::new(AtomicBool::new(false)), + // thread_running: Arc::new(AtomicBool::new(false)), + }; + ctrl.do_initialization()?; + + Ok(ctrl) + } + + fn do_initialization(&self) -> Result<(), SlashCtlError> { + + let init_packets = pkts_for_init(); + self.node.write_bytes(&init_packets[0])?; + self.node.write_bytes(&init_packets[1])?; + + Ok(()) + } + + pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), SlashCtlError> { + 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<(), SlashCtlError> { + let command_packets = pkt_set_mode(slash_mode as u8); + self.node.write_bytes(&command_packets[0])?; + self.node.write_bytes(&command_packets[1])?; + Ok(()) + } +} \ No newline at end of file diff --git a/slashctl/src/error.rs b/slashctl/src/error.rs new file mode 100644 index 00000000..f32cdb14 --- /dev/null +++ b/slashctl/src/error.rs @@ -0,0 +1,30 @@ +use std::fmt; +use std::fmt::{Display}; +use rog_slash::error::SlashError; + +#[derive(Debug)] +pub enum SlashCtlError { + NotSupported, + Slash(SlashError), +} + + +impl Display for SlashCtlError { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SlashCtlError::NotSupported => write!(f, "Not supported"), + SlashCtlError::Slash(err) => write!(f, "Slash error: {}", err), + } + } +} + +impl std::error::Error for SlashCtlError {} + + + +impl From for SlashCtlError { + fn from(err: SlashError) -> Self { + SlashCtlError::Slash(err) + } +} \ No newline at end of file diff --git a/slashctl/src/main.rs b/slashctl/src/main.rs new file mode 100644 index 00000000..cbe682a5 --- /dev/null +++ b/slashctl/src/main.rs @@ -0,0 +1,15 @@ +use crate::ctrl_slash::CtrlSlash; +use crate::error::SlashCtlError; + +mod ctrl_slash; +mod error; + +fn main() -> Result<(), SlashCtlError>{ + // let args: Vec = args().skip(1).collect(); + + let ctrl_slash = CtrlSlash::new()?; + ctrl_slash.set_options(false, 10, 0)?; + // ctrl_slash.set_options(true, 5, 2)?; + // ctrl_slash.set_slash_mode(SlashModes::Flow)?; + Ok(()) +} \ No newline at end of file