Partial daemon mode for builtin LED control

This commit is contained in:
Luke
2020-04-16 15:26:13 +12:00
parent 0d5341e003
commit 2d4953d87b
10 changed files with 265 additions and 63 deletions

22
Cargo.lock generated
View File

@@ -42,6 +42,16 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "dbus"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add8dd36d6d34a084220eb9fe216d3e230d52b37c31702e1ffda4fb2d4ef950e"
dependencies = [
"libc",
"libdbus-sys",
]
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.9" version = "0.2.9"
@@ -80,6 +90,15 @@ version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
[[package]]
name = "libdbus-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
dependencies = [
"pkg-config",
]
[[package]] [[package]]
name = "libflate" name = "libflate"
version = "0.1.27" version = "0.1.27"
@@ -144,8 +163,9 @@ checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
[[package]] [[package]]
name = "rog-core" name = "rog-core"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"dbus",
"gumdrop", "gumdrop",
"rusb", "rusb",
] ]

View File

@@ -7,3 +7,4 @@ edition = "2018"
[dependencies] [dependencies]
rusb = "0.5" rusb = "0.5"
gumdrop = "0.8" gumdrop = "0.8"
dbus = "0.7.1"

59
Makefile Normal file
View File

@@ -0,0 +1,59 @@
prefix ?= /usr
sysconfdir ?= /etc
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
libdir = $(exec_prefix)/lib
includedir = $(prefix)/include
datarootdir = $(prefix)/share
datadir = $(datarootdir)
SRC = Cargo.toml Cargo.lock Makefile $(shell find src -type f -wholename '*src/*.rs')
.PHONY: all clean distclean install uninstall update
BIN=rog-core
DEBUG ?= 0
ifeq ($(DEBUG),0)
ARGS += "--release"
TARGET = release
endif
VENDORED ?= 0
ifeq ($(VENDORED),1)
ARGS += "--frozen"
endif
all: target/release/$(BIN)
clean:
cargo clean
distclean:
rm -rf .cargo vendor vendor.tar.xz
install: all
install -D -m 04755 "target/release/$(BIN)" "$(DESTDIR)$(bindir)/$(BIN)"
install -D -m 0644 "data/$(BIN).conf" "$(DESTDIR)$(sysconfdir)/dbus-1/system.d/$(BIN).conf"
install -D -m 0644 "data/$(BIN).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN).service"
uninstall:
rm -f "$(DESTDIR)$(bindir)/$(BIN)"
rm -f "$(DESTDIR)$(sysconfdir)/dbus-1/system.d/$(BIN).conf"
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN).service"
update:
cargo update
vendor:
mkdir -p .cargo
cargo vendor | head -n -1 > .cargo/config
echo 'directory = "vendor"' >> .cargo/config
tar pcfJ vendor.tar.xz vendor
rm -rf vendor
target/release/$(BIN): $(SRC)
ifeq ($(VENDORED),1)
tar pxf vendor.tar.xz
endif
cargo build $(ARGS)

18
data/rog-core.conf Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy group="adm">
<allow send_destination="org.rogcore.Daemon"/>
<allow receive_sender="org.rogcore.Daemon"/>
</policy>
<policy group="sudo">
<allow send_destination="org.rogcore.Daemon"/>
<allow receive_sender="org.rogcore.Daemon"/>
</policy>
<policy user="root">
<allow own="org.rogcore.Daemon"/>
<allow send_destination="org.rogcore.Daemon"/>
<allow receive_sender="org.rogcore.Daemon"/>
</policy>
</busconfig>

9
data/rog-core.service Normal file
View File

@@ -0,0 +1,9 @@
[Unit]
Description=ROG Core Daemon
[Service]
ExecStart=/usr/bin/rog-core -d
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -1,45 +1,11 @@
use crate::core::LED_MSG_LEN; use crate::core::LED_MSG_LEN;
use crate::error::AuraError;
use gumdrop::Options; use gumdrop::Options;
use std::error::Error; use std::fmt::Debug;
use std::fmt;
use std::fmt::{Debug, Display};
use std::str::FromStr; use std::str::FromStr;
#[derive(PartialEq)]
pub enum AuraError {
ParseColour,
ParseSpeed,
ParseDirection,
ParseBrightness,
}
impl Debug for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Display for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Error for AuraError {
fn description(&self) -> &str {
match self {
AuraError::ParseColour => "could not parse colour",
AuraError::ParseSpeed => "could not parse speed",
AuraError::ParseDirection => "could not parse direction",
AuraError::ParseBrightness => "could not parse brightness",
}
}
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Colour(u8, u8, u8); pub(crate) struct Colour(u8, u8, u8);
impl Default for Colour { impl Default for Colour {
fn default() -> Self { fn default() -> Self {
Colour(255, 0, 0) Colour(255, 0, 0)
@@ -60,7 +26,7 @@ impl FromStr for Colour {
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Speed { pub(crate) enum Speed {
Low = 0xe1, Low = 0xe1,
Med = 0xeb, Med = 0xeb,
High = 0xf5, High = 0xf5,
@@ -88,7 +54,7 @@ impl FromStr for Speed {
/// ///
/// Enum corresponds to the required integer value /// Enum corresponds to the required integer value
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Direction { pub(crate) enum Direction {
Right, Right,
Left, Left,
Up, Up,
@@ -115,7 +81,7 @@ impl FromStr for Direction {
} }
#[derive(Debug, PartialEq, Options)] #[derive(Debug, PartialEq, Options)]
pub struct Breathe { pub(crate) struct Breathe {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(no_long, help = "set the first colour, must be hex string e.g, ff00ff")] #[options(no_long, help = "set the first colour, must be hex string e.g, ff00ff")]
@@ -130,7 +96,7 @@ pub struct Breathe {
} }
#[derive(Debug, PartialEq, Options)] #[derive(Debug, PartialEq, Options)]
pub struct SingleSpeed { pub(crate) struct SingleSpeed {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(no_long, help = "set the speed: low, med, high")] #[options(no_long, help = "set the speed: low, med, high")]
@@ -138,7 +104,7 @@ pub struct SingleSpeed {
} }
#[derive(Debug, PartialEq, Options)] #[derive(Debug, PartialEq, Options)]
pub struct SingleColour { pub(crate) struct SingleColour {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(no_long, help = "set the colour, must be hex string e.g, ff00ff")] #[options(no_long, help = "set the colour, must be hex string e.g, ff00ff")]
@@ -146,7 +112,7 @@ pub struct SingleColour {
} }
#[derive(Debug, PartialEq, Options)] #[derive(Debug, PartialEq, Options)]
pub struct SingleSpeedDirection { pub(crate) struct SingleSpeedDirection {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(no_long, help = "set the direction: up, down, left, right")] #[options(no_long, help = "set the direction: up, down, left, right")]
@@ -156,7 +122,7 @@ pub struct SingleSpeedDirection {
} }
#[derive(Debug, PartialEq, Options)] #[derive(Debug, PartialEq, Options)]
pub struct SingleColourSpeed { pub(crate) struct SingleColourSpeed {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(no_long, help = "set the colour, must be hex string e.g, ff00ff")] #[options(no_long, help = "set the colour, must be hex string e.g, ff00ff")]
@@ -169,7 +135,7 @@ pub struct SingleColourSpeed {
/// ///
/// Enum corresponds to the required integer value /// Enum corresponds to the required integer value
#[derive(Debug, Options)] #[derive(Debug, Options)]
pub enum SetAuraBuiltin { pub(crate) enum SetAuraBuiltin {
#[options(help = "set a single static colour")] #[options(help = "set a single static colour")]
Stable(SingleColour), Stable(SingleColour),
#[options(help = "pulse between one or two colours")] #[options(help = "pulse between one or two colours")]
@@ -247,7 +213,7 @@ impl Default for SetAuraBuiltin {
/// - 0x03 = downwards /// - 0x03 = downwards
/// ///
/// Bytes 10, 11, 12 are Red, Green, Blue for second colour if mode supports it /// Bytes 10, 11, 12 are Red, Green, Blue for second colour if mode supports it
pub struct ModeMessage(pub [u8; LED_MSG_LEN]); pub(crate) struct ModeMessage(pub [u8; LED_MSG_LEN]);
impl From<SetAuraBuiltin> for ModeMessage { impl From<SetAuraBuiltin> for ModeMessage {
fn from(mode: SetAuraBuiltin) -> Self { fn from(mode: SetAuraBuiltin) -> Self {

View File

@@ -1,5 +1,7 @@
use crate::aura::AuraError; use crate::error::AuraError;
use crate::aura::ModeMessage; use crate::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
use dbus::Error as DbusError;
use dbus::{ffidisp::Connection, Message};
use gumdrop::Options; use gumdrop::Options;
use rusb::{DeviceHandle, Error}; use rusb::{DeviceHandle, Error};
use std::str::FromStr; use std::str::FromStr;
@@ -17,7 +19,7 @@ static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
#[derive(Debug, Options)] #[derive(Debug, Options)]
pub struct LedBrightness { pub(crate) struct LedBrightness {
pub level: u8, pub level: u8,
} }
impl FromStr for LedBrightness { impl FromStr for LedBrightness {
@@ -47,7 +49,7 @@ impl FromStr for LedBrightness {
/// - `LED_INIT4` /// - `LED_INIT4`
/// - `LED_INIT2` /// - `LED_INIT2`
/// - `LED_INIT4` /// - `LED_INIT4`
pub struct RogCore { pub(crate) struct RogCore {
handle: DeviceHandle<rusb::GlobalContext>, handle: DeviceHandle<rusb::GlobalContext>,
initialised: bool, initialised: bool,
led_interface_num: u8, led_interface_num: u8,
@@ -96,7 +98,7 @@ impl RogCore {
Err(Error::NoDevice) Err(Error::NoDevice)
} }
fn aura_write_messages(&mut self, messages: &[[u8; LED_MSG_LEN]]) -> Result<(), Error> { fn aura_write_messages(&mut self, messages: &[&[u8]]) -> Result<(), Error> {
self.handle.claim_interface(self.led_interface_num)?; self.handle.claim_interface(self.led_interface_num)?;
// Declared as a zoomy so that it is hidden // Declared as a zoomy so that it is hidden
let write = |message: &[u8]| { let write = |message: &[u8]| {
@@ -114,7 +116,7 @@ impl RogCore {
for message in messages { for message in messages {
println!("{:x?}", &message); println!("{:x?}", &message);
write(message)?; write(*message)?;
write(&LED_SET)?; write(&LED_SET)?;
} }
// Changes won't persist unless apply is set // Changes won't persist unless apply is set
@@ -124,19 +126,31 @@ impl RogCore {
Ok(()) Ok(())
} }
pub fn aura_set_brightness(&mut self, brightness: u8) -> Result<(), Error> { pub fn aura_brightness_bytes(brightness: u8) -> Result<[u8; 17], Error> {
let mut bright = [0u8; LED_MSG_LEN]; let mut bright = [0u8; LED_MSG_LEN];
bright[0] = 0x5a; bright[0] = 0x5a;
bright[1] = 0xba; bright[1] = 0xba;
bright[2] = 0xc5; bright[2] = 0xc5;
bright[3] = 0xc4; bright[3] = 0xc4;
bright[4] = brightness; bright[4] = brightness;
let messages = [bright]; Ok(bright)
self.aura_write_messages(&messages)
} }
pub fn aura_set_mode(&mut self, mode: ModeMessage) -> Result<(), Error> { pub fn aura_set_mode(&mut self, mode: &[u8]) -> Result<(), Error> {
let messages = [mode.0]; let messages = [mode];
self.aura_write_messages(&messages) self.aura_write_messages(&messages)
} }
} }
pub(super) fn dbus_led_builtin_write(bytes: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let bus = Connection::new_system()?;
//let proxy = bus.with_proxy(DBUS_IFACE, "/", Duration::from_millis(5000));
let msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ledmessage")?
.append1(bytes.to_vec());
let r = bus.send_with_reply_and_block(msg, 5000)?;
if let Some(reply) = r.get1::<&str>() {
println!("Daemon sez: {}", reply);
return Ok(());
}
Err(Box::new(DbusError::new_custom("name", "message")))
}

55
src/daemon.rs Normal file
View File

@@ -0,0 +1,55 @@
use crate::{core::RogCore, DBUS_IFACE, DBUS_PATH};
use dbus::{
blocking::Connection,
tree::{Factory, MethodErr},
};
use std::error::Error;
use std::time::Duration;
pub fn start_daemon() -> Result<(), Box<dyn Error>> {
let mut c = Connection::new_system()?;
c.request_name(DBUS_IFACE, false, true, false)?;
// The choice of factory tells us what type of tree we want,
// and if we want any extra data inside. We pick the simplest variant.
let f = Factory::new_fn::<()>();
// We create a tree with one object path inside and make that path introspectable.
let tree = f.tree(()).add(
f.object_path(DBUS_PATH, ()).introspectable().add(
// We add an interface to the object path...
f.interface(DBUS_IFACE, ())
// ...and a method inside the interface
.add_m(
f.method("ledmessage", (), move |m| {
// Reads the args passed to the method
// Reads the args passed to the method
let bytes: Vec<u8> = m.msg.read1()?;
let s = format!("Shoving {:x?} in to keyboard!", bytes);
if let Ok(mut core) = RogCore::new() {
match core.aura_set_mode(&bytes[..]) {
Ok(_) => {
let mret = m.msg.method_return().append1(s);
Ok(vec![mret])
}
Err(err) => Err(MethodErr::failed(&err)),
}
} else {
Err(MethodErr::failed("Failed to start RogCore"))
}
})
// Input?
.outarg::<&str, _>("reply")
.inarg::<Vec<u8>, _>("bytearray"),
),
),
);
// We add the tree to the connection so that incoming method calls will be handled.
tree.start_receive(&c);
loop {
c.process(Duration::from_secs(1))?;
}
}

36
src/error.rs Normal file
View File

@@ -0,0 +1,36 @@
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display};
#[derive(PartialEq)]
pub(crate) enum AuraError {
ParseColour,
ParseSpeed,
ParseDirection,
ParseBrightness,
}
impl Debug for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Display for AuraError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.description(), f)
}
}
impl Error for AuraError {
fn description(&self) -> &str {
match self {
AuraError::ParseColour => "could not parse colour",
AuraError::ParseSpeed => "could not parse speed",
AuraError::ParseDirection => "could not parse direction",
AuraError::ParseBrightness => "could not parse brightness",
}
}
}

View File

@@ -1,15 +1,26 @@
// TODO: use /sys/class/dmi/id/board_name to detect model // TODO: use /sys/class/dmi/id/board_name to detect model
mod aura; mod aura;
mod core; mod core;
mod daemon;
mod error;
use crate::aura::*; use crate::aura::*;
use crate::core::{LedBrightness, RogCore}; use crate::core::{dbus_led_builtin_write, LedBrightness, RogCore};
use crate::daemon::*;
use gumdrop::Options; use gumdrop::Options;
pub static DBUS_NAME: &'static str = "org.rogcore.Daemon";
pub static DBUS_PATH: &'static str = "/org/rogcore/Daemon";
pub static DBUS_IFACE: &'static str = "org.rogcore.Daemon";
#[derive(Debug, Options)] #[derive(Debug, Options)]
struct CLIStart { struct CLIStart {
#[options(help = "print help message")] #[options(help = "print help message")]
help: bool, help: bool,
#[options(help = "start daemon")]
daemon: bool,
#[options(help = "client mode")]
client: bool,
#[options(help = "<off, low, med, high>")] #[options(help = "<off, low, med, high>")]
bright: Option<LedBrightness>, bright: Option<LedBrightness>,
#[options(command)] #[options(command)]
@@ -32,12 +43,20 @@ struct LedModeCommand {
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let parsed = CLIStart::parse_args_default_or_exit(); let parsed = CLIStart::parse_args_default_or_exit();
if parsed.daemon {
start_daemon()?;
}
match parsed.command { match parsed.command {
Some(Command::LedMode(okk)) => match okk.command { Some(Command::LedMode(okk)) => match okk.command {
Some(command) => { Some(command) => {
let mut core = RogCore::new()?;
let mode = ModeMessage::from(command); let mode = ModeMessage::from(command);
core.aura_set_mode(mode)?; if parsed.client {
dbus_led_builtin_write(&mode.0)?;
} else {
let mut core = RogCore::new()?;
core.aura_set_mode(&mode.0)?;
}
} }
_ => {} _ => {}
}, },
@@ -45,8 +64,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
match parsed.bright { match parsed.bright {
Some(brightness) => { Some(brightness) => {
let mut core = RogCore::new()?; let bytes = RogCore::aura_brightness_bytes(brightness.level as u8)?;
core.aura_set_brightness(brightness.level as u8)?; if parsed.client {
dbus_led_builtin_write(&bytes)?;
} else {
let mut core = RogCore::new()?;
core.aura_set_mode(&bytes)?;
}
} }
_ => {} _ => {}
} }