mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-23 01:43:19 +01:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
703bba9ffd | ||
|
|
73706154a4 | ||
|
|
c9b2a0c777 | ||
|
|
54cc51fe5d | ||
|
|
81645d0777 | ||
|
|
d61c180ee5 | ||
|
|
a668800fd9 | ||
|
|
3bdc11c994 | ||
|
|
b496139063 | ||
|
|
607483629a | ||
|
|
5c8d138cef | ||
|
|
e3db1f7c4c | ||
|
|
de3b803f14 | ||
|
|
0558f919c4 | ||
|
|
68ea73c847 | ||
|
|
96ddb7132d | ||
|
|
d36ac44603 | ||
|
|
5d06c87943 | ||
|
|
588e3c0102 | ||
|
|
6ce32c1cab | ||
|
|
a23c51e5db | ||
|
|
3845071ba3 | ||
|
|
a48b3634bf | ||
|
|
806882b2e9 | ||
|
|
9ac3c46fe6 | ||
|
|
6817a1c027 | ||
|
|
cf2be1b12b | ||
|
|
9ffeb19c8c | ||
|
|
a7f6e8bd24 | ||
|
|
f61f62c219 | ||
|
|
bb70fd7b71 | ||
|
|
b80c860b7a | ||
|
|
3c7544f034 | ||
|
|
6746c2b654 | ||
|
|
8d59f89438 | ||
|
|
5753160e91 | ||
|
|
622cd9d943 | ||
|
|
2daa7f0811 | ||
|
|
05c53df0ed | ||
|
|
a7419cbc4c | ||
|
|
67ad38a7e6 | ||
|
|
e572ae2c62 | ||
|
|
24962eedc1 | ||
|
|
368d279ca5 | ||
|
|
9e4cc329ed | ||
|
|
d4702a166a | ||
|
|
925c709097 | ||
|
|
411788f72c | ||
|
|
4e4ea0035e | ||
|
|
0063f3f0fc | ||
|
|
fe6231ad4e | ||
|
|
4cdb06959b | ||
|
|
781ad30eb5 | ||
|
|
84d056f2ed | ||
|
|
3a2f2c99f4 | ||
|
|
cddff32757 | ||
|
|
1b427c6c07 | ||
|
|
6ba645f727 | ||
|
|
772538bc8a | ||
|
|
855d4dc701 | ||
|
|
3095e181eb | ||
|
|
ee020085f8 | ||
|
|
b70137eec8 | ||
|
|
aa4d79c6f5 | ||
|
|
ab8393a807 | ||
|
|
b8c83a1a72 | ||
|
|
8cee0e6735 | ||
|
|
c482208d3c | ||
|
|
708712e00a | ||
|
|
25ee250542 | ||
|
|
b921b68c57 |
26
.gitlab-ci.yml
Normal file
26
.gitlab-ci.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
image: rustdocker/rust:stable
|
||||
|
||||
before_script:
|
||||
- apt-get update -qq && apt-get install -y -qq libdbus-1-dev libclang-dev libudev-dev
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
|
||||
test:
|
||||
script:
|
||||
- cargo check #+nightly check --features "clippy"
|
||||
|
||||
build:
|
||||
only:
|
||||
- next
|
||||
script:
|
||||
- make && make vendor
|
||||
artifacts:
|
||||
paths:
|
||||
- vendor_asus-nb-ctrl_*.tar.xz
|
||||
- cargo-config
|
||||
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: normal
|
||||
|
||||
69
CHANGELOG.md
69
CHANGELOG.md
@@ -6,6 +6,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
# [2.1.2] - 2021-01-10
|
||||
### Changed
|
||||
- Adjust gfx controller to assume that the graphics driver is loaded if the
|
||||
mode is set for nvidia/hybrid
|
||||
|
||||
# [2.1.1] - 2021-01-09
|
||||
### Changed
|
||||
- Updates to dependencies
|
||||
|
||||
# [2.1.0] - 2020-10-25
|
||||
### Added
|
||||
- Option to turn off AniMe display (@asere)
|
||||
### Changed
|
||||
- Change option -k to show current LED bright (@asere)
|
||||
- Correctly disable GFX control via config
|
||||
- Panic and exit if config can't be parsed
|
||||
- Add DBUS method to toggle to next fan/thermal profile
|
||||
- Add DBUS method to toggle to next/prev Aura mode
|
||||
|
||||
# [2.0.5] - 2020-09-29
|
||||
### Changed
|
||||
- Bugfixes
|
||||
|
||||
# [2.0.4] - 2020-09-24
|
||||
### Changed
|
||||
- Better and more verbose error handling and logging in many places.
|
||||
- Fix timeout for client waiting on reply for graphics switching
|
||||
|
||||
# [2.0.2] - 2020-09-21
|
||||
### Changed
|
||||
- graphics options via CLI are now a command block:
|
||||
+ `asusctl graphics`
|
||||
+ -m Mode <nvidia, hybrid, compute, integrated>
|
||||
+ -g Get current mode
|
||||
+ -f Force reboot or restart display manager without confirmation
|
||||
|
||||
# [2.0.0] - 2020-09-21
|
||||
### Changed
|
||||
- Code refactor to spawn less tasks. Main loop will run only as fast as
|
||||
it receives events
|
||||
- No-longer using tokio or async, reducing resource use
|
||||
### Added
|
||||
- A basic user daemon has been added for user notifications over dbus (XDG spec)
|
||||
- Added a user systemd service for notifications (asus-notify)
|
||||
- Graphics mode handling <iGPU only, dGPU only, or hybrid>, see asusctl --help
|
||||
### BREAKING CHANGES
|
||||
- asusd.conf has changed slightly and will overwrite old configs
|
||||
- All DBUS methods/signals/paths etc, are all updated and changed
|
||||
|
||||
# [1.1.2] - 2020-09-10
|
||||
### Changed
|
||||
- Bump rog-fan-curve to new versiont o support GA401IV
|
||||
|
||||
# [1.1.1] - 2020-09-10
|
||||
### Changed
|
||||
- Correction to AMD turbo setting
|
||||
|
||||
# [1.1.0] - 2020-09-10
|
||||
### Changed
|
||||
- Uses string instead of debug print for some errors
|
||||
- Add interface num arg for LED controller (should help support
|
||||
older laptops better)
|
||||
- Some slightly better error messages
|
||||
- Fix an idiotic mistake in `for i in 0..2.. if i > 0` -_-
|
||||
- Remove "unsupported" warning on laptop ctrl
|
||||
- Silence warning about AniMe not existing
|
||||
- Adjust the turbo-toggle CLI arg
|
||||
- Version bump for new release with fancurves
|
||||
|
||||
## [1.0.2] - 2020-08-13
|
||||
### Changed
|
||||
- Bugfixes to led brightness watcher
|
||||
|
||||
1017
Cargo.lock
generated
1017
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["asus-nb-ctrl", "asus-nb"]
|
||||
members = ["asus-notify", "asus-nb-ctrl", "asus-nb", "ctrl-gfx"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -13,4 +13,4 @@ opt-level = 1
|
||||
|
||||
[profile.bench]
|
||||
debug = false
|
||||
opt-level = 3
|
||||
opt-level = 3
|
||||
|
||||
81
Makefile
81
Makefile
@@ -1,32 +1,37 @@
|
||||
prefix ?= /usr
|
||||
sysconfdir ?= /etc
|
||||
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2)
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
|
||||
INSTALL_DATA = ${INSTALL} -D -m 0644
|
||||
|
||||
prefix = /usr
|
||||
exec_prefix = $(prefix)
|
||||
bindir = $(exec_prefix)/bin
|
||||
libdir = $(exec_prefix)/lib
|
||||
includedir = $(prefix)/include
|
||||
datarootdir = $(prefix)/share
|
||||
datadir = $(datarootdir)
|
||||
libdir = $(exec_prefix)/lib
|
||||
zshcpl = $(datarootdir)/zsh/site-functions
|
||||
|
||||
SRC = Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
||||
BIN_C := asusctl
|
||||
BIN_D := asusd
|
||||
BIN_N := asus-notify
|
||||
LEDCFG := asusd-ledmodes.toml
|
||||
X11CFG := 90-nvidia-screen-G05.conf
|
||||
PMRULES := 90-asusd-nvidia-pm.rules
|
||||
|
||||
.PHONY: all clean distclean install uninstall update
|
||||
|
||||
BIN_C=asusctl
|
||||
BIN_D=asusd
|
||||
LEDCONFIG=asusd-ledmodes.toml
|
||||
SRC := Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
|
||||
|
||||
DEBUG ?= 0
|
||||
ifeq ($(DEBUG),0)
|
||||
ARGS += "--release"
|
||||
ARGS += --release
|
||||
TARGET = release
|
||||
endif
|
||||
|
||||
VENDORED ?= 0
|
||||
ifeq ($(VENDORED),1)
|
||||
ARGS += "--frozen"
|
||||
ARGS += --frozen
|
||||
endif
|
||||
|
||||
all: target/release/$(BIN_D)
|
||||
all: build
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
@@ -34,20 +39,37 @@ clean:
|
||||
distclean:
|
||||
rm -rf .cargo vendor vendor.tar.xz
|
||||
|
||||
install: all
|
||||
install -D -m 0755 "target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
install -D -m 0755 "target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
install -D -m 0644 "data/$(BIN_D).rules" "$(DESTDIR)/lib/udev/rules.d/99-$(BIN_D).rules"
|
||||
install -D -m 0644 "data/$(LEDCONFIG)" "$(DESTDIR)$(sysconfdir)/asusd/$(LEDCONFIG)"
|
||||
install -D -m 0644 "data/$(BIN_D).conf" "$(DESTDIR)$(sysconfdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||
install -D -m 0644 "data/$(BIN_D).service" "$(DESTDIR)/lib/systemd/system/$(BIN_D).service"
|
||||
install:
|
||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
$(INSTALL_PROGRAM) "./target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||
$(INSTALL_DATA) "./data/$(PMRULES)" "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
|
||||
$(INSTALL_DATA) "./data/$(BIN_D).rules" "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
||||
$(INSTALL_DATA) "./data/$(LEDCFG)" "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
||||
$(INSTALL_DATA) "./data/$(BIN_D).conf" "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||
$(INSTALL_DATA) "./data/$(X11CFG)" "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
|
||||
$(INSTALL_DATA) "./data/$(BIN_D).service" "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
||||
$(INSTALL_DATA) "./data/$(BIN_N).service" "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
||||
$(INSTALL_DATA) "./data/icons/asus_notif_yellow.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
||||
$(INSTALL_DATA) "./data/icons/asus_notif_green.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
||||
$(INSTALL_DATA) "./data/icons/asus_notif_red.png" "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
||||
$(INSTALL_DATA) "./data/_asusctl" "$(DESTDIR)$(zshcpl)/_asusctl"
|
||||
|
||||
uninstall:
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
rm -f "$(DESTDIR)/lib/udev/rules.d/99-$(BIN_D).rules"
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||
rm -f "$(DESTDIR)/lib/systemd/system/$(BIN_D).service"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/$(PMRULES)"
|
||||
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
||||
rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
||||
rm -f "$(DESTDIR)$(datarootdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||
rm -f "$(DESTDIR)$(datarootdir)/X11/xorg.conf.d/$(X11CFG)"
|
||||
rm -f "$(DESTDIR)$(libdir)/systemd/system/$(BIN_D).service"
|
||||
rm -r "$(DESTDIR)$(libdir)/systemd/user/$(BIN_N).service"
|
||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_green.png"
|
||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/asus_notif_red.png"
|
||||
rm -f "$(DESTDIR)$(zshcpl)/_asusctl"
|
||||
|
||||
update:
|
||||
cargo update
|
||||
@@ -56,11 +78,16 @@ vendor:
|
||||
mkdir -p .cargo
|
||||
cargo vendor | head -n -1 > .cargo/config
|
||||
echo 'directory = "vendor"' >> .cargo/config
|
||||
tar pcfJ vendor.tar.xz vendor
|
||||
mv .cargo/config ./cargo-config
|
||||
rm -rf .cargo
|
||||
tar pcfJ vendor_asus-nb-ctrl_$(VERSION).tar.xz vendor
|
||||
rm -rf vendor
|
||||
|
||||
target/release/$(BIN_D): $(SRC)
|
||||
build:
|
||||
ifeq ($(VENDORED),1)
|
||||
tar pxf vendor.tar.xz
|
||||
@echo "version = $(VERSION)"
|
||||
tar pxf vendor_asus-nb-ctrl_$(VERSION).tar.xz
|
||||
endif
|
||||
cargo build $(ARGS)
|
||||
|
||||
.PHONY: all clean distclean install uninstall update build
|
||||
|
||||
280
README.md
280
README.md
@@ -1,9 +1,15 @@
|
||||
# ASUS NB Ctrl
|
||||
|
||||
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
|
||||
but can also be used with non-asus laptops with reduced features.
|
||||
|
||||
**NOTICE:**
|
||||
|
||||
This program requires the kernel patch in `./kernel-patch/` to be applied.
|
||||
As of 04/08/2020 these have been submitted to lkml.
|
||||
This app is developed and tested on fedora only. Support is not provided for Arch or Arch based distros.
|
||||
|
||||
This program requires the kernel patch [here](https://www.spinics.net/lists/linux-input/msg68977.html) to be applied.
|
||||
Alternatively you may use the dkms module for 'hid-asus-rog` from one of the
|
||||
repositories [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/).
|
||||
|
||||
The patch enables the following in kernel:
|
||||
|
||||
@@ -11,73 +17,86 @@ The patch enables the following in kernel:
|
||||
- Control of keyboard brightness using FN+Key combos (not RGB)
|
||||
- FN+F5 (fan) to toggle fan modes
|
||||
|
||||
You will not get RGB control in kernel (yet), and asusd is still required to
|
||||
change modes and RGB settings. The previous version of this program is named
|
||||
`rog-core` and takes full control of the interfaces required - if you can't
|
||||
apply the kernel patches then `rog-core` is still highly usable.
|
||||
You will not get RGB control in kernel (yet), and `asusd` + `asusctl` is required
|
||||
to change modes and RGB settings.
|
||||
|
||||
Many other patches for these laptops, AMD and Intel based, are working their way
|
||||
in to the kernel.
|
||||
|
||||
---
|
||||
asusd is a utility for Linux to control many aspects of various ASUS laptops.
|
||||
|
||||
## Discord
|
||||
|
||||
[Discord server link](https://discord.gg/PVyFzWj)
|
||||
[Discord server link](https://discord.gg/ngbdKabAnP)
|
||||
|
||||
## SUPPORTED LAPTOPS
|
||||
|
||||
If your laptop is not in the following lists, it may still work with fan-mode switching and charge limit control.
|
||||
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
|
||||
to this:
|
||||
|
||||
**Please help test or provide info for:**
|
||||
```
|
||||
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
```
|
||||
|
||||
- GL703(0x1869)
|
||||
- GL553/GL753 (device = 0x1854) (attempted support from researching 2nd-hand info, multizone may work)
|
||||
then it may work without tweaks. Technically all other functions except the LED
|
||||
and AniMe parts should work regardless of your latop make. Eventually this project
|
||||
will probably suffer another rename once it becomes generic enough to do so.
|
||||
|
||||
**Laptop support is modified on a per-case basis** as the EC for the keyboard varies
|
||||
a little between models, e.g, some RGB modes are missing, or it's a single colour.
|
||||
As far as I can see, the EC does not give us a way to find what modes are supported.
|
||||
## Implemented
|
||||
|
||||
### ANIME AND OTHER FUNCTIONS
|
||||
- [X] System daemon
|
||||
- [X] User notifications daemon
|
||||
- [X] Setting/modifying built-in LED modes
|
||||
- [X] Per-key LED setting
|
||||
- [X] Fancy LED modes (See examples)
|
||||
- [X] Saving settings for reload
|
||||
- [X] Logging - required for journalctl
|
||||
- [X] AniMatrix display on G14 models that include it
|
||||
- [X] Set battery charge limit (with kernel supporting this)
|
||||
- [X] Fancy fan control on G14 + G15 thanks to @Yarn1
|
||||
- [X] Graphics mode switching between iGPU, dGPU, and On-Demand
|
||||
|
||||
**AniMe device check is performed on start, if your device has one it will be detected.**
|
||||
# FUNCTIONS
|
||||
|
||||
**NOTE:** If charge limit or fan modes are not working, then you may require a kernel newer than 5.6.10.
|
||||
## Graphics switching
|
||||
|
||||
- [X] AniMe Matrix display
|
||||
- [X] Power profile switching on fan-mode (FN+F5)
|
||||
- [X] Intel
|
||||
- [X] Turbo enale/disable
|
||||
- [X] Min frequency percentage
|
||||
- [X] Max frequency percentage
|
||||
- [X] AMD
|
||||
- [X] Turbo enale/disable
|
||||
- [X] Battery charge limit
|
||||
A new feature has been added to enable switching graphics modes. This can be disabled
|
||||
in the config with `"manage_gfx": false,`. Please be aware it is a work in progress.
|
||||
|
||||
**NOTE:** GA14/GA401 and GA15/GA502/GU502, You will need kernel [patches](https://lab.retarded.farm/zappel/asus-rog-zephyrus-g14/-/tree/master/kernel_patches).
|
||||
The CLI option for this does not require root until it asks for it, and provides
|
||||
instructions.
|
||||
|
||||
### KEYBOARD BACKLIGHT MODES
|
||||
This switcher conflicts with other gpu switchers like optimus-manager, suse-prime
|
||||
or ubuntu-prime, system76-power, and bbswitch. If you have issues with `asusd`
|
||||
always defaulting to `integrated` mode on boot then you will need to check for
|
||||
stray configs blocking nvidia modules from loading in:
|
||||
- `/etc/modprobe.d/`
|
||||
- `/usr/lib/modprope.d/`
|
||||
|
||||
### Power management udev rule
|
||||
|
||||
If you have installed the Nvidia driver manually you will require the
|
||||
`data/90-asusd-nvidia-pm.rules` udev rule to be installed in `/etc/udev/rules.d/`.
|
||||
|
||||
### fedora and openSUSE
|
||||
|
||||
You *may* need a file `/etc/dracut.conf.d/90-nvidia-dracut-G05.conf` installed
|
||||
to stop dracut including the nvidia modules in the ramdisk.
|
||||
|
||||
```
|
||||
# filename /etc/dracut.conf.d/90-nvidia-dracut-G05.conf
|
||||
# Omit the nvidia driver from the ramdisk, to avoid needing to regenerate
|
||||
# the ramdisk on updates, and to ensure the power-management udev rules run
|
||||
# on module load
|
||||
omit_drivers+=" nvidia nvidia-drm nvidia-modeset nvidia-uvm "
|
||||
```
|
||||
|
||||
and run `dracut -f` after creating it.
|
||||
|
||||
## KEYBOARD BACKLIGHT MODES
|
||||
|
||||
Models GA401, GA502, GU502 support LED brightness change only (no RGB).
|
||||
|
||||
| MODEL | STATIC | BREATHING | STROBE | RAINBOW | STAR | RAIN | HIGHLIGHT | LASER | RIPPLE | PULSE | COMET | FLASH | ZONES | PER-KEY RGB |
|
||||
|:------:|:------:|:---------:|:------:|:-------:|:----:|:----:|:---------:|:-----:|:------:|:-----:|:-----:|:-----:|:-----:|:-----------:|
|
||||
| G512LI | X | X | X | X | | | | | | | | | | |
|
||||
| G712LI | X | X | X | X | | | | | | | | | | |
|
||||
| GM501 | X | X | X | X | | | | | | | | | X | |
|
||||
| GX531 | X | X | X | X | | | | | | | | | X | |
|
||||
| G512 | X | X | X | X | | | | | | | | | X | |
|
||||
| G712 | X | X | X | X | | | | | | | | | X | |
|
||||
| GX502 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
| GX701 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
| G531 | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
||||
| G731 | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
||||
| G532 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
|
||||
It is highly likely this doesn't cover all models.
|
||||
|
||||
For editing the `/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as follows:
|
||||
If you model isn't getting the correct led modes, you can edit the file
|
||||
`/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as follows:
|
||||
|
||||
```
|
||||
0 STATIC
|
||||
@@ -96,28 +115,40 @@ For editing the `/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as fo
|
||||
255 PER_KEY
|
||||
```
|
||||
|
||||
## Implemented
|
||||
use `cat /sys/class/dmi/id/product_name` to get details about your laptop.
|
||||
|
||||
- [X] Daemon
|
||||
- [X] Setting/modifying built-in LED modes
|
||||
- [X] Per-key LED setting
|
||||
- [X] Fancy LED modes (See examples)
|
||||
- [X] Saving settings for reload
|
||||
- [X] Logging - required for journalctl
|
||||
- [X] AniMatrix display on G14 models that include it
|
||||
- [X] Set battery charge limit (with kernel supporting this)
|
||||
# Keybinds
|
||||
|
||||
## Requirements for compiling
|
||||
To switch to next/previous Aura modes you will need to bind both the aura keys (if available) to one of:
|
||||
**Next**
|
||||
```
|
||||
asusctl led-mode -n
|
||||
```
|
||||
**Previous**
|
||||
```
|
||||
asusctl led-mode -p
|
||||
```
|
||||
|
||||
- `rustc` + `cargo` + `make`
|
||||
- `libusb-1.0-0-dev`
|
||||
- `libdbus-1-dev`
|
||||
- `llvm`
|
||||
- `libclang-dev`
|
||||
- `libudev-dev`
|
||||
To switch Fan/Thermal profiles you need to bind the Fn+F5 key to `asusctl profile -n`.
|
||||
|
||||
# BUILDING
|
||||
|
||||
Requirements are rust >= 1.40 installed from rustup.io if the distro provided version is too old, and `make`.
|
||||
|
||||
**Ubuntu*:** `apt install libdbus-1-dev libclang-dev libudev-dev`
|
||||
|
||||
**fedora:** `dnf install clang-devel dbus-devel systemd-devel`
|
||||
|
||||
## Installing
|
||||
|
||||
Packaging and auto-builds are available [here](https://build.opensuse.org/package/show/home:luke_nukem:asus/asus-nb-ctrl)
|
||||
|
||||
Download repositories are available [here](https://download.opensuse.org/repositories/home:/luke_nukem:/asus/)
|
||||
|
||||
Alternatively check the releases page for f33 RPM.
|
||||
|
||||
---
|
||||
|
||||
Run `make` then `sudo make install` then reboot.
|
||||
|
||||
The default init method is to use the udev rule, this ensures that the service is
|
||||
@@ -129,32 +160,36 @@ If you are upgrading from a previous installed version, you will need to restart
|
||||
$ systemctl daemon-reload && systemctl restart asusd
|
||||
```
|
||||
|
||||
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power`
|
||||
gnome-shell extension, or at least limiting use of the power-management parts as `asusd` lets you set the same things
|
||||
(one or the other will overwrite pstates). I will create a shell extension at some point similar to system76, but using
|
||||
the asusd parts. It is safe to leave `system76-power.service` enabled and use for switching between graphics modes.
|
||||
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
|
||||
|
||||
If you would like to run this daemon on another non-ASUS laptop you can. You'll
|
||||
have all features available except the LED and AniMe control (further controllers
|
||||
can be added on request). You will need to install the alternative service from
|
||||
`data/asusd-alt.service`.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd.conf`.
|
||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
|
||||
|
||||
## Updating
|
||||
|
||||
Occasionally you need to remove `/etc/asusd.conf` and restart the daemon to create a new one. You *can* back up the old
|
||||
one and copy settings back over (then restart daemon again).
|
||||
If there has been a config file format change your config will be overwritten. This will
|
||||
become less of an issue once the feature set is nailed down. Work is happening to enable
|
||||
parsing of older configs and transferring settings to new.
|
||||
|
||||
# Usage
|
||||
# USAGE
|
||||
|
||||
**NOTE! Fan mode toggling requires a newer kernel**. I'm unsure when the patches required for it got merged - I've
|
||||
tested with the 5.6.6 kernel and above only. To see if the fan-mode changed cat either:
|
||||
**NOTE! Fan mode toggling requires a newer kernel**. I'm unsure when the patches
|
||||
required for it got merged - I've tested with the 5.6.6 kernel and above only.
|
||||
To see if the fan-mode changed cat either:
|
||||
|
||||
- `cat /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy` or
|
||||
- `cat /sys/devices/platform/asus-nb-wmi/fan_boost_mode`
|
||||
|
||||
The numbers are 0 = Normal/Balanced, 1 = Boost, 2 = Silent.
|
||||
|
||||
Running the program as a daemon manually will require root. Standard (non-daemon) mode expects to be communicating with
|
||||
the daemon mode over dbus.
|
||||
Running the program as a daemon manually will require root. Standard (non-daemon)
|
||||
mode expects to be communicating with the daemon mode over dbus.
|
||||
|
||||
Commands are given by:
|
||||
|
||||
@@ -175,60 +210,6 @@ Some commands may have subcommands:
|
||||
asusctl <command> <subcommand> --help
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
$ asusctl --help
|
||||
Usage: asusctl [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-v, --version show program version number
|
||||
-k, --kbd-bright VAL <off, low, med, high>
|
||||
-p, --pwr-profile PWR <silent, normal, boost>
|
||||
-c, --chg-limit CHRG <20-100>
|
||||
|
||||
Available commands:
|
||||
led-mode Set the keyboard lighting from built-in modes
|
||||
|
||||
$ asusctl led-mode --help
|
||||
Usage: asusctl led-mode [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
|
||||
Available commands:
|
||||
static set a single static colour
|
||||
breathe pulse between one or two colours
|
||||
strobe strobe through all colours
|
||||
rainbow rainbow cycling in one of four directions
|
||||
star rain pattern mimicking raindrops
|
||||
rain rain pattern of three preset colours
|
||||
highlight pressed keys are highlighted to fade
|
||||
laser pressed keys generate horizontal laser
|
||||
ripple pressed keys ripple outwards like a splash
|
||||
pulse set a rapid pulse
|
||||
comet set a vertical line zooming from left
|
||||
flash set a wide vertical line zooming from left
|
||||
multi-static 4-zone multi-colour
|
||||
|
||||
$ asusctl led-mode static --help
|
||||
Usage: asusctl led-mode static [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-c HEX set the RGB value e.g, ff00ff
|
||||
|
||||
$ asusctl led-mode star --help
|
||||
Usage: asusctl led-mode star [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-c HEX set the first RGB value e.g, ff00ff
|
||||
-C HEX set the second RGB value e.g, ff00ff
|
||||
-s SPEED set the speed: low, med, high
|
||||
```
|
||||
|
||||
## Daemon mode
|
||||
|
||||
If the daemon service is enabled then on boot the following will be reloaded from save:
|
||||
@@ -242,27 +223,48 @@ The daemon also saves the settings per mode as the keyboard does not do this
|
||||
itself - this means cycling through modes with the Aura keys will use the
|
||||
settings that were used via CLI.
|
||||
|
||||
Daemon mode creates a config file at `/etc/asusd.conf` which you can edit a
|
||||
Daemon mode creates a config file at `/etc/asusd/asusd.conf` which you can edit a
|
||||
little of. Most parts will be byte arrays, but you can adjust things like
|
||||
`mode_performance`.
|
||||
|
||||
### DBUS Input
|
||||
## User NOTIFICATIONS via dbus
|
||||
|
||||
If you have a notifications handler set up, or are using KDE or Gnome then you
|
||||
can enable the user service to get basic notifications when something changes.
|
||||
|
||||
```
|
||||
systemctl --user enable asus-notify.service
|
||||
systemctl --user start asus-notify.service
|
||||
```
|
||||
# OTHER
|
||||
|
||||
## DBUS Input
|
||||
|
||||
See [README_DBUS.md](./README_DBUS.md).
|
||||
|
||||
### AniMe input
|
||||
## AniMe input
|
||||
|
||||
You will want to look at what MeuMeu has done with [https://github.com/Meumeu/ZephyrusBling/](https://github.com/Meumeu/ZephyrusBling/)
|
||||
|
||||
### Wireshark captures
|
||||
|
||||
TODO: see `./wireshark_data/` for some captures.
|
||||
|
||||
### Supporting more laptops
|
||||
## Supporting more laptops
|
||||
|
||||
Please file a support request.
|
||||
|
||||
## License
|
||||
## Notes:
|
||||
|
||||
- If charge limit or fan modes are not working, then you may require a kernel newer than 5.6.10.
|
||||
- AniMe device check is performed on start, if your device has one it will be detected.
|
||||
- GA14/GA401 and GA15/GA502/GU502, You will need kernel [patches](https://lab.retarded.farm/zappel/asus-rog-zephyrus-g14/-/tree/master/kernel_patches), these are on their way to the kernel upstream.
|
||||
- On fedora manually installed Nvidia driver requires a dracut config as follows:
|
||||
```
|
||||
# filename/etc/dracut.conf.d/90-nvidia-dracut-G05.conf
|
||||
# Omit the nvidia driver from the ramdisk, to avoid needing to regenerate
|
||||
# the ramdisk on updates, and to ensure the power-management udev rules run
|
||||
# on module load
|
||||
omit_drivers+=" nvidia nvidia-drm nvidia-modeset nvidia-uvm "
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
Mozilla Public License 2 (MPL-2.0)
|
||||
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
# DBUS Guide
|
||||
|
||||
```rust
|
||||
pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
||||
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
|
||||
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||
```
|
||||
**WARNING: In progress updates**
|
||||
|
||||
## Methods
|
||||
Interface name = org.asuslinux.Daemon
|
||||
|
||||
- `SetKeyBacklight`
|
||||
- `GetKeyBacklight`
|
||||
- `AnimatrixWrite`
|
||||
- `SetFanMode`
|
||||
- `GetFanMode`
|
||||
- `SetChargeLimit`
|
||||
- `GetChargeLimit`
|
||||
- `GetKeyBacklightModes`
|
||||
Paths:
|
||||
- `/org/asuslinux/Gfx`
|
||||
+ `SetVendor` (string)
|
||||
+ `NotifyVendor` (recv vendor label string)
|
||||
- `/org/asuslinux/Led`
|
||||
+ `LedMode` (AuraMode as json)
|
||||
+ `LedModes` (array[AuraMode] as json)
|
||||
+ `SetLedMode` (AuraMode -> json)
|
||||
+ `NotifyLed` (recv json data)
|
||||
- `/org/asuslinux/Anime`
|
||||
+ `SetAnime` (byte array data)
|
||||
- `/org/asuslinux/Charge`
|
||||
+ `Limit` (u8)
|
||||
+ `SetLimit` (u8)
|
||||
+ `NotifyCharge` (recv i8)
|
||||
- `/org/asuslinux/Profile`
|
||||
+ `Profile` (recv current profile data as json string)
|
||||
+ `Profiles` (recv profiles data as json string (map))
|
||||
+ `SetProfile` (event -> json)
|
||||
+ `NotifyProfile` (recv current profile name)
|
||||
|
||||
## Signals
|
||||
All `Notify*` methods are signals.
|
||||
|
||||
- `KeyBacklightChanged`
|
||||
- `FanModeChanged`
|
||||
- `ChargeLimitChanged`
|
||||
|
||||
## Method Inputs
|
||||
|
||||
### SetKeyBacklight
|
||||
### SetLed
|
||||
|
||||
This method expects a string of JSON as input. The JSON is of format such:
|
||||
|
||||
@@ -66,23 +68,6 @@ dbus.
|
||||
|
||||
Lastly, there is `"LedBrightness": <u8>` which accepts 0-3 for off, low, med, high.
|
||||
|
||||
### GetKeyBacklight
|
||||
|
||||
This method will return a JSON string in the same format as accepted by `SetKeyBacklight`.
|
||||
|
||||
### GetKeyBacklightModes
|
||||
|
||||
Will return a JSON string array of modes that this laptop accepts. The mode data
|
||||
within this will be the current settings per mode. Good for:
|
||||
|
||||
- Getting supported modes
|
||||
- Getting all mode settings
|
||||
|
||||
### AnimatrixWrite
|
||||
|
||||
Used to write data to the AniMe display if available. Currently is takes `[[u8; 640]; 2]`
|
||||
which must be the byte data that will be written directly to the USB device.
|
||||
|
||||
### SetFanMode
|
||||
|
||||
Accepts an integer from the following:
|
||||
@@ -91,41 +76,13 @@ Accepts an integer from the following:
|
||||
- `1`: Boost mode
|
||||
- `2`: Silent mode
|
||||
|
||||
### GetFanMode
|
||||
|
||||
Returns the integer set from above.
|
||||
|
||||
### SetChargeLimit
|
||||
|
||||
Accepts an integer in the range of 20-100.
|
||||
|
||||
### GetChargeLimit
|
||||
|
||||
Returns the integer set from above.
|
||||
|
||||
## Signal Outs
|
||||
|
||||
### KeyBacklightChanged
|
||||
|
||||
When emitted, it will emit the JSON data of the mode changed to, e.g:
|
||||
## dbus-send examples:
|
||||
|
||||
```
|
||||
{
|
||||
"Static": {
|
||||
"colour": [ 255, 0, 0]
|
||||
}
|
||||
}
|
||||
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Profile org.asuslinux.Daemon.NextProfile
|
||||
```
|
||||
|
||||
### FanModeChanged
|
||||
|
||||
When emitted, it will include the integer the fan mode was changed to.
|
||||
|
||||
### ChargeLimitChanged
|
||||
|
||||
When emitted, it will include the integer the charging limit was changed to.
|
||||
|
||||
## dbus-send examples
|
||||
## dbus-send examples OUTDATED
|
||||
|
||||
```
|
||||
dbus-send --system --type=method_call --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.asuslinux.Daemon.SetKeyBacklight string:'{"Static": {"colour": [ 80, 0, 40]}}'
|
||||
@@ -154,5 +111,5 @@ Monitoring dbus while sending commands via `rog-core` will give you the json str
|
||||
## Getting an introspection .xml
|
||||
|
||||
```
|
||||
dbus-send --system --print-reply --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.freedesktop.DBus.Introspectable.Introspect > xml/dbus-0.14.4.xml
|
||||
dbus-send --system --print-reply --dest=org.asuslinux.Daemon /org/asuslinux/Charge org.freedesktop.DBus.Introspectable.Introspect > xml/asusd-charge.xml
|
||||
```
|
||||
7
TODO.md
Normal file
7
TODO.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# TODO
|
||||
|
||||
- There is lots of code duplication. This should be turned in to macros (dbus stuff etc)
|
||||
- Add a little more information to profile notifications such as freq min/max, fan curves
|
||||
- Finish splitting out controllers to own crates
|
||||
- Finish move to zbus in client when zbus has client signal watch
|
||||
- Consider a rename again because the project is getting a lot less ASUS centric
|
||||
@@ -1,70 +0,0 @@
|
||||
%if %{defined fedora}
|
||||
%global debug_package %{nil}
|
||||
%endif
|
||||
|
||||
# Use hardening ldflags.
|
||||
%global rustflags -Clink-arg=-Wl,-z,relro,-z,now
|
||||
Name: asus-nb-ctrl
|
||||
Version: 1.0.0
|
||||
Release: 0
|
||||
Summary: Text editor for terminal
|
||||
License: MPLv2
|
||||
Group: Productivity/Text/Editors
|
||||
URL: https://gitlab.com/asus-linux/asus-nb-ctrl
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
# cargo vendor &&
|
||||
# tar cfJ vendor.tar.xz vendor
|
||||
Source1: vendor.tar.xz
|
||||
BuildRequires: clang-devel
|
||||
BuildRequires: cargo
|
||||
BuildRequires: rust
|
||||
BuildRequires: rust-std-static
|
||||
BuildRequires: pkgconfig(dbus-1)
|
||||
BuildRequires: pkgconfig(libudev)
|
||||
|
||||
%description
|
||||
ASUS Laptop control
|
||||
|
||||
%prep
|
||||
%setup -q -n %name-next
|
||||
%setup -q -n %name-next -D -T -a 1
|
||||
|
||||
mkdir .cargo
|
||||
cat >.cargo/config <<EOF
|
||||
[source.crates-io]
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source.vendored-sources]
|
||||
directory = "vendor"
|
||||
EOF
|
||||
|
||||
%build
|
||||
export RUSTFLAGS="%{rustflags}"
|
||||
RUST_BACKTRACE=1 cargo build --release
|
||||
|
||||
%install
|
||||
export RUSTFLAGS="%{rustflags}"
|
||||
|
||||
mkdir -p "%{buildroot}%{_bindir}"
|
||||
install -D -m 0755 target/release/asusd %{buildroot}%{_bindir}/asusd
|
||||
install -D -m 0755 target/release/asusctl %{buildroot}%{_bindir}/asusctl
|
||||
install -D -m 0644 data/asusd.rules %{buildroot}%{_udevrulesdir}/90-asusd.rules
|
||||
install -D -m 0644 data/asusd.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/asusd.conf
|
||||
install -D -m 0644 data/asusd.service %{buildroot}%{_unitdir}/asusd.service
|
||||
install -D -m 0644 data/asusd-ledmodes.toml %{buildroot}%{_sysconfdir}/asusd/asusd-ledmodes.toml
|
||||
|
||||
mkdir -p "%{buildroot}%{_datadir}/licenses/%{name}"
|
||||
cp LICENSE "%{buildroot}%{_datadir}/licenses/%{name}/"
|
||||
|
||||
mkdir -p "%{buildroot}/bin"
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%{_bindir}/asusd
|
||||
%{_bindir}/asusctl
|
||||
%{_unitdir}/asusd.service
|
||||
%{_udevrulesdir}/90-asusd.rules
|
||||
%{_sysconfdir}/dbus-1/system.d/asusd.conf
|
||||
%{_sysconfdir}/asusd/asusd-ledmodes.toml
|
||||
|
||||
%changelog
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "asus-nb-ctrl"
|
||||
version = "1.0.2"
|
||||
version = "2.1.2"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -22,20 +22,18 @@ name = "asusd"
|
||||
path = "src/daemon.rs"
|
||||
|
||||
[dependencies]
|
||||
ctrl-gfx = { path = "../ctrl-gfx" }
|
||||
asus-nb = { path = "../asus-nb" }
|
||||
rusb = "^0.6.0"
|
||||
udev = "^0.4.0"
|
||||
async-trait = "0.1.36"
|
||||
|
||||
# cli and logging
|
||||
gumdrop = "^0.8.0"
|
||||
log = "^0.4.8"
|
||||
env_logger = "^0.7.1"
|
||||
|
||||
# async
|
||||
dbus = { version = "^0.8.2", features = ["futures"] }
|
||||
dbus-tokio = "^0.5.1"
|
||||
tokio = { version = "^0.2.4", features = ["rt-threaded", "sync"] }
|
||||
zbus = "^1.8.0"
|
||||
zvariant = "^2.4.0"
|
||||
|
||||
# serialisation
|
||||
serde = "^1.0"
|
||||
@@ -45,5 +43,8 @@ toml = "0.4.6"
|
||||
|
||||
# Device control
|
||||
sysfs-class = "^0.1.2" # used for backlight control and baord ID
|
||||
rog_fan_curve = { version = "0.1.5", features = ["serde"] }
|
||||
# cpu power management
|
||||
intel-pstate = "^0.2.1"
|
||||
intel-pstate = "^0.2.1"
|
||||
|
||||
yansi-term = "^0.1"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use asus_nb::aura_modes::AuraModes;
|
||||
use log::{error, warn};
|
||||
use rog_fan_curve::Curve;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
@@ -8,24 +10,30 @@ pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub gfx_managed: bool,
|
||||
pub active_profile: String,
|
||||
pub toggle_profiles: Vec<String>,
|
||||
// TODO: remove power_profile
|
||||
pub power_profile: u8,
|
||||
pub bat_charge_limit: u8,
|
||||
pub kbd_led_brightness: u8,
|
||||
pub kbd_backlight_mode: u8,
|
||||
pub kbd_backlight_modes: Vec<AuraModes>,
|
||||
pub power_profiles: FanModeProfile,
|
||||
pub power_profiles: BTreeMap<String, Profile>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// `load` will attempt to read the config, but if it is not found it
|
||||
/// will create a new default config and write that out.
|
||||
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||
pub fn load(mut self, supported_led_modes: &[u8]) -> Self {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&CONFIG_PATH)
|
||||
.unwrap(); // okay to cause panic here
|
||||
.expect(&format!(
|
||||
"The file {} or directory /etc/asusd/ is missing",
|
||||
CONFIG_PATH
|
||||
)); // okay to cause panic here
|
||||
let mut buf = String::new();
|
||||
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||
if l == 0 {
|
||||
@@ -33,7 +41,7 @@ impl Config {
|
||||
} else {
|
||||
self = serde_json::from_str(&buf).unwrap_or_else(|_| {
|
||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
||||
Config::create_default(&mut file, &supported_led_modes)
|
||||
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -42,20 +50,41 @@ impl Config {
|
||||
|
||||
fn create_default(file: &mut File, supported_led_modes: &[u8]) -> Self {
|
||||
// create a default config here
|
||||
let mut c = Config::default();
|
||||
c.bat_charge_limit = 100;
|
||||
c.kbd_backlight_mode = 0;
|
||||
c.kbd_led_brightness = 1;
|
||||
let mut config = Config::default();
|
||||
config.gfx_managed = true;
|
||||
|
||||
config.bat_charge_limit = 100;
|
||||
config.kbd_backlight_mode = 0;
|
||||
config.kbd_led_brightness = 1;
|
||||
|
||||
for n in supported_led_modes {
|
||||
c.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
config.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
}
|
||||
|
||||
let mut profile = Profile::default();
|
||||
profile.fan_preset = 0;
|
||||
profile.turbo = true;
|
||||
config.power_profiles.insert("normal".into(), profile);
|
||||
|
||||
let mut profile = Profile::default();
|
||||
profile.fan_preset = 1;
|
||||
profile.turbo = true;
|
||||
config.power_profiles.insert("boost".into(), profile);
|
||||
|
||||
let mut profile = Profile::default();
|
||||
profile.fan_preset = 2;
|
||||
config.power_profiles.insert("silent".into(), profile);
|
||||
|
||||
config.toggle_profiles.push("normal".into());
|
||||
config.toggle_profiles.push("boost".into());
|
||||
config.toggle_profiles.push("silent".into());
|
||||
config.active_profile = "normal".into();
|
||||
|
||||
// Should be okay to unwrap this as is since it is a Default
|
||||
let json = serde_json::to_string_pretty(&c).unwrap();
|
||||
let json = serde_json::to_string_pretty(&config).unwrap();
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
||||
c
|
||||
config
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
@@ -75,6 +104,17 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_new() -> Result<Config, Box<dyn std::error::Error>> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&CONFIG_PATH)
|
||||
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)?;
|
||||
let x: Config = serde_json::from_str(&buf)?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
pub fn write(&self) {
|
||||
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
|
||||
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||
@@ -103,26 +143,26 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct FanModeProfile {
|
||||
pub normal: CPUSettings,
|
||||
pub boost: CPUSettings,
|
||||
pub silent: CPUSettings,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CPUSettings {
|
||||
pub struct Profile {
|
||||
pub min_percentage: u8,
|
||||
pub max_percentage: u8,
|
||||
pub no_turbo: bool,
|
||||
pub turbo: bool,
|
||||
pub fan_preset: u8,
|
||||
pub fan_curve: Option<Curve>,
|
||||
}
|
||||
|
||||
impl Default for CPUSettings {
|
||||
#[deprecated]
|
||||
pub type CPUSettings = Profile;
|
||||
|
||||
impl Default for Profile {
|
||||
fn default() -> Self {
|
||||
CPUSettings {
|
||||
Profile {
|
||||
min_percentage: 0,
|
||||
max_percentage: 100,
|
||||
no_turbo: false,
|
||||
turbo: false,
|
||||
fan_preset: 0,
|
||||
fan_curve: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,25 +6,27 @@ const DEV_PAGE: u8 = 0x5e;
|
||||
// These bytes are in [1] position of the array
|
||||
const WRITE: u8 = 0xc0;
|
||||
const INIT: u8 = 0xc2;
|
||||
const APPLY: u8 = 0xc3;
|
||||
const SET: u8 = 0xc4;
|
||||
const SET: u8 = 0xc3;
|
||||
const APPLY: u8 = 0xc4;
|
||||
|
||||
// Used to turn the panel on and off
|
||||
// The next byte can be 0x03 for "on" and 0x00 for "off"
|
||||
const ON_OFF: u8 = 0x04;
|
||||
|
||||
use crate::config::Config;
|
||||
use asus_nb::error::AuraError;
|
||||
use log::{error, info, warn};
|
||||
use rusb::{Device, DeviceHandle};
|
||||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum AnimatrixCommand {
|
||||
Apply,
|
||||
Set,
|
||||
SetBoot(bool),
|
||||
Write(Vec<u8>),
|
||||
WriteImage(Vec<Vec<u8>>),
|
||||
//ReloadLast,
|
||||
}
|
||||
@@ -34,32 +36,64 @@ pub struct CtrlAnimeDisplay {
|
||||
initialised: bool,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
//AnimatrixWrite
|
||||
pub trait Dbus {
|
||||
fn set_anime(&mut self, input: Vec<Vec<u8>>);
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlAnimeDisplay {
|
||||
type A = Vec<Vec<u8>>;
|
||||
fn set_on_off(&mut self, status: bool);
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
mut self,
|
||||
_: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
_: Option<Arc<SyncConnection>>,
|
||||
_: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
vec![tokio::spawn(async move {
|
||||
while let Some(image) = recv.recv().await {
|
||||
self.do_command(AnimatrixCommand::WriteImage(image))
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
})]
|
||||
fn set_boot_on_off(&mut self, status: bool);
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for CtrlAnimeDisplay {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Anime".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl Dbus for CtrlAnimeDisplay {
|
||||
fn set_anime(&mut self, input: Vec<Vec<u8>>) {
|
||||
self.do_command(AnimatrixCommand::WriteImage(input))
|
||||
.map_or_else(|err| warn!("{}", err), |()| info!("Writing image to Anime"));
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, _: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
fn set_on_off(&mut self, status: bool) {
|
||||
let mut flush: Vec<u8> = vec![0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = WRITE;
|
||||
flush[2] = ON_OFF;
|
||||
|
||||
let status_str;
|
||||
if status {
|
||||
flush[3] = 0x03;
|
||||
status_str = "on";
|
||||
} else {
|
||||
flush[3] = 0x00;
|
||||
status_str = "off";
|
||||
}
|
||||
|
||||
self.do_command(AnimatrixCommand::Write(flush)).map_or_else(
|
||||
|err| warn!("{}", err),
|
||||
|()| info!("Turning {} the AniMe", status_str),
|
||||
);
|
||||
}
|
||||
|
||||
fn set_boot_on_off(&mut self, status: bool) {
|
||||
let status_str = if status { "on" } else { "off" };
|
||||
|
||||
self.do_command(AnimatrixCommand::SetBoot(status))
|
||||
.and_then(|()| self.do_command(AnimatrixCommand::Apply))
|
||||
.map_or_else(
|
||||
|err| warn!("{}", err),
|
||||
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,21 +101,18 @@ impl CtrlAnimeDisplay {
|
||||
#[inline]
|
||||
pub fn new() -> Result<CtrlAnimeDisplay, Box<dyn Error>> {
|
||||
// We don't expect this ID to ever change
|
||||
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b).map_err(|err| {
|
||||
warn!("Could not get AniMe display handle: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b)?;
|
||||
|
||||
let mut device = device.open()?;
|
||||
device.reset()?;
|
||||
|
||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||
error!("Auto-detach kernel driver failed: {:?}", err);
|
||||
error!("Auto-detach kernel driver failed: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
device.claim_interface(0).map_err(|err| {
|
||||
error!("Could not claim device interface: {:?}", err);
|
||||
error!("Could not claim device interface: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
@@ -103,15 +134,17 @@ impl CtrlAnimeDisplay {
|
||||
Err(rusb::Error::NoDevice)
|
||||
}
|
||||
|
||||
pub async fn do_command(&mut self, command: AnimatrixCommand) -> Result<(), AuraError> {
|
||||
pub fn do_command(&mut self, command: AnimatrixCommand) -> Result<(), AuraError> {
|
||||
if !self.initialised {
|
||||
self.do_initialization().await?
|
||||
self.do_initialization()?
|
||||
}
|
||||
|
||||
match command {
|
||||
AnimatrixCommand::WriteImage(effect) => self.write_image(effect).await?,
|
||||
AnimatrixCommand::Set => self.do_set().await?,
|
||||
AnimatrixCommand::Apply => self.do_apply().await?,
|
||||
AnimatrixCommand::Apply => self.do_apply()?,
|
||||
//AnimatrixCommand::Set => self.do_set_boot()?,
|
||||
AnimatrixCommand::SetBoot(status) => self.do_set_boot(status)?,
|
||||
AnimatrixCommand::Write(bytes) => self.write_bytes(&bytes)?,
|
||||
AnimatrixCommand::WriteImage(effect) => self.write_image(effect)?,
|
||||
//AnimatrixCommand::ReloadLast => self.reload_last_builtin(&config).await?,
|
||||
}
|
||||
Ok(())
|
||||
@@ -119,8 +152,7 @@ impl CtrlAnimeDisplay {
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
#[inline]
|
||||
async fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
|
||||
let prev = std::time::Instant::now();
|
||||
fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
|
||||
match self.handle.write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
@@ -129,15 +161,10 @@ impl CtrlAnimeDisplay {
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
) {
|
||||
Ok(_) => {
|
||||
println!(
|
||||
"{:?}",
|
||||
std::time::Instant::now().duration_since(prev).as_micros()
|
||||
);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(err) => match err {
|
||||
rusb::Error::Timeout => {}
|
||||
_ => error!("Failed to write to led interrupt: {:?}", err),
|
||||
_ => error!("Failed to write to led interrupt: {}", err),
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@@ -159,22 +186,22 @@ impl CtrlAnimeDisplay {
|
||||
///
|
||||
/// Where led brightness is 0..255, low to high
|
||||
#[inline]
|
||||
async fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||
fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||
for row in image.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
self.do_flush().await?;
|
||||
self.do_flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_initialization(&mut self) -> Result<(), AuraError> {
|
||||
fn do_initialization(&mut self) -> Result<(), AuraError> {
|
||||
let mut init = [0; PACKET_SIZE];
|
||||
init[0] = DEV_PAGE; // This is the USB page we're using throughout
|
||||
for (idx, byte) in INIT_STR.as_bytes().iter().enumerate() {
|
||||
init[idx + 1] = *byte
|
||||
}
|
||||
self.write_bytes(&init).await?;
|
||||
self.write_bytes(&init)?;
|
||||
|
||||
// clear the init array and write other init message
|
||||
for ch in init.iter_mut() {
|
||||
@@ -183,43 +210,43 @@ impl CtrlAnimeDisplay {
|
||||
init[0] = DEV_PAGE; // write it to be sure?
|
||||
init[1] = INIT;
|
||||
|
||||
self.write_bytes(&init).await?;
|
||||
self.write_bytes(&init)?;
|
||||
self.initialised = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_flush(&mut self) -> Result<(), AuraError> {
|
||||
fn do_flush(&mut self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = WRITE;
|
||||
flush[2] = 0x03;
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
self.write_bytes(&flush)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_set(&mut self) -> Result<(), AuraError> {
|
||||
fn do_set_boot(&mut self, status: bool) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = SET;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = 0x80;
|
||||
flush[3] = if status { 0x00 } else { 0x80 };
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
self.write_bytes(&flush)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_apply(&mut self) -> Result<(), AuraError> {
|
||||
fn do_apply(&mut self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = APPLY;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = 0x80;
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
self.write_bytes(&flush)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,93 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use crate::{config::Config, error::RogError};
|
||||
//use crate::dbus::DbusEvents;
|
||||
use log::{info, warn};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
|
||||
|
||||
pub struct CtrlCharge {
|
||||
path: &'static str,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlCharge {
|
||||
type A = u8;
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
_: Option<Arc<SyncConnection>>,
|
||||
_: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
vec![tokio::spawn(async move {
|
||||
while let Some(n) = recv.recv().await {
|
||||
let mut config = config.lock().await;
|
||||
self.set_charge_limit(n, &mut config)
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
}
|
||||
})]
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlCharge {
|
||||
pub fn set_limit(&mut self, limit: u8) {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
self.set(limit, &mut config)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
self.notify_charge(limit)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: set_limit {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
config.read();
|
||||
info!("Reloaded battery charge limit");
|
||||
self.set_charge_limit(config.bat_charge_limit, config)
|
||||
pub fn limit(&self) -> i8 {
|
||||
if let Ok(config) = self.config.try_lock() {
|
||||
return config.bat_charge_limit as i8;
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn notify_charge(&self, limit: u8) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for CtrlCharge {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Charge".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlCharge {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
config.read();
|
||||
info!("Reloaded battery charge limit");
|
||||
self.set(config.bat_charge_limit, &mut config)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlCharge {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
let path = CtrlCharge::get_battery_path()?;
|
||||
info!("Device has battery charge threshold control");
|
||||
Ok(CtrlCharge { path })
|
||||
Ok(CtrlCharge { path, config })
|
||||
}
|
||||
|
||||
fn get_battery_path() -> Result<&'static str, std::io::Error> {
|
||||
fn get_battery_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(BAT_CHARGE_PATH).exists() {
|
||||
Ok(BAT_CHARGE_PATH)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"Charge control not available",
|
||||
Err(RogError::MissingFunction(
|
||||
"Charge control not available, you may require a v5.8.10 series kernel or newer"
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_charge_limit(
|
||||
&self,
|
||||
limit: u8,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
if limit < 20 || limit > 100 {
|
||||
warn!(
|
||||
"Unable to set battery charge limit, must be between 20-100: requested {}",
|
||||
@@ -79,12 +98,9 @@ impl CtrlCharge {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to open battery charge limit path: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
file.write_all(limit.to_string().as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", BAT_CHARGE_PATH, err));
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
config.read();
|
||||
|
||||
@@ -1,232 +1,372 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use crate::config::{Config, Profile};
|
||||
use asus_nb::profile::ProfileEvent;
|
||||
use log::{info, warn};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
|
||||
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
|
||||
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
|
||||
|
||||
pub struct CtrlFanAndCPU {
|
||||
path: &'static str,
|
||||
pub path: &'static str,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
pub struct DbusFanAndCpu {
|
||||
inner: Arc<Mutex<CtrlFanAndCPU>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlFanAndCPU {
|
||||
type A = u8;
|
||||
impl DbusFanAndCpu {
|
||||
pub fn new(inner: Arc<Mutex<CtrlFanAndCPU>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
_: Option<Arc<SyncConnection>>,
|
||||
_: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
let gate1 = Arc::new(Mutex::new(self));
|
||||
let gate2 = gate1.clone();
|
||||
let config1 = config.clone();
|
||||
// spawn an endless loop
|
||||
vec![
|
||||
tokio::spawn(async move {
|
||||
while let Some(mode) = recv.recv().await {
|
||||
let mut config = config1.lock().await;
|
||||
let mut lock = gate1.lock().await;
|
||||
lock.set_fan_mode(mode, &mut config)
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl DbusFanAndCpu {
|
||||
/// Set profile details
|
||||
fn set_profile(&self, profile: String) {
|
||||
if let Ok(event) = serde_json::from_str(&profile) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
cfg.read();
|
||||
ctrl.handle_profile_event(&event, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_profile(&cfg.active_profile)
|
||||
.unwrap_or_else(|_| ());
|
||||
}
|
||||
}),
|
||||
// need to watch file path
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::delay_for(std::time::Duration::from_millis(100)).await;
|
||||
let mut lock = gate2.lock().await;
|
||||
let mut config = config.lock().await;
|
||||
lock.fan_mode_check_change(&mut config)
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = OpenOptions::new().write(true).open(self.path)?;
|
||||
file.write_all(format!("{:?}\n", config.power_profile).as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", self.path, err));
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(config.power_profile), config)?;
|
||||
info!(
|
||||
"Reloaded fan mode: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
/// Fetch the active profile name
|
||||
fn next_profile(&mut self) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
ctrl.do_next_profile(&mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
||||
if let Ok(json) = serde_json::to_string(profile) {
|
||||
self.notify_profile(&json)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the active profile name
|
||||
fn active_profile_name(&mut self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
||||
cfg.read();
|
||||
return cfg.active_profile.clone();
|
||||
}
|
||||
}
|
||||
"Failed".to_string()
|
||||
}
|
||||
|
||||
/// Fetch the active profile details
|
||||
fn profile(&mut self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
||||
cfg.read();
|
||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
||||
if let Ok(json) = serde_json::to_string(profile) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"Failed".to_string()
|
||||
}
|
||||
|
||||
fn profiles(&mut self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
||||
cfg.read();
|
||||
if let Ok(json) = serde_json::to_string(&cfg.power_profiles) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
"Failed".to_string()
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_profile(&self, profile: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for DbusFanAndCpu {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Profile".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("DbusFanAndCpu: {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlFanAndCPU {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
file.write_all(format!("{}\n", config.power_profile).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
let profile = config.active_profile.clone();
|
||||
self.set(&profile, &mut config)?;
|
||||
info!(
|
||||
"Reloaded fan mode: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CtrlTask for CtrlFanAndCPU {
|
||||
fn do_task(&mut self) -> Result<(), RogError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read(self.path.into(), err))?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
if config.power_profile != num as u8 {
|
||||
config.read();
|
||||
|
||||
let mut i = config
|
||||
.toggle_profiles
|
||||
.iter()
|
||||
.position(|x| x == &config.active_profile)
|
||||
.map(|i| i + 1)
|
||||
.unwrap_or(0);
|
||||
if i >= config.toggle_profiles.len() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
let new_profile = config
|
||||
.toggle_profiles
|
||||
.get(i)
|
||||
.unwrap_or(&config.active_profile)
|
||||
.clone();
|
||||
|
||||
self.set(&new_profile, &mut config)?;
|
||||
|
||||
info!("Profile was changed: {}", &new_profile);
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(RogError::DoTask("Fan-level could not be parsed".into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlFanAndCPU {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
let path = CtrlFanAndCPU::get_fan_path()?;
|
||||
info!("Device has thermal throttle control");
|
||||
Ok(CtrlFanAndCPU { path })
|
||||
Ok(CtrlFanAndCPU { path, config })
|
||||
}
|
||||
|
||||
fn get_fan_path() -> Result<&'static str, std::io::Error> {
|
||||
fn get_fan_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(FAN_TYPE_1_PATH).exists() {
|
||||
Ok(FAN_TYPE_1_PATH)
|
||||
} else if Path::new(FAN_TYPE_2_PATH).exists() {
|
||||
Ok(FAN_TYPE_2_PATH)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"Fan mode not available",
|
||||
Err(RogError::MissingFunction(
|
||||
"Fan mode not available, you may require a v5.8.10 series kernel or newer".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn fan_mode_check_change(
|
||||
&mut self,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = OpenOptions::new().read(true).open(self.path)?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if config.power_profile != num as u8 {
|
||||
config.read();
|
||||
config.power_profile = num as u8;
|
||||
config.write();
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(config.power_profile), config)?;
|
||||
info!(
|
||||
"Fan mode was changed: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Fan-level could not be parsed",
|
||||
);
|
||||
Err(Box::new(err))
|
||||
}
|
||||
|
||||
pub(super) fn set_fan_mode(
|
||||
&mut self,
|
||||
n: u8,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut fan_ctrl = OpenOptions::new().write(true).open(self.path)?;
|
||||
/// Toggle to next profile in list
|
||||
pub(super) fn do_next_profile(&mut self, config: &mut Config) -> Result<(), RogError> {
|
||||
config.read();
|
||||
config.power_profile = n;
|
||||
config.write();
|
||||
fan_ctrl
|
||||
.write_all(format!("{:?}\n", config.power_profile).as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", self.path, err));
|
||||
info!(
|
||||
"Fan mode set to: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(n), config)?;
|
||||
|
||||
let mut i = config
|
||||
.toggle_profiles
|
||||
.iter()
|
||||
.position(|x| x == &config.active_profile)
|
||||
.map(|i| i + 1)
|
||||
.unwrap_or(0);
|
||||
if i >= config.toggle_profiles.len() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
let new_profile = config
|
||||
.toggle_profiles
|
||||
.get(i)
|
||||
.unwrap_or(&config.active_profile)
|
||||
.clone();
|
||||
|
||||
self.set(&new_profile, config)?;
|
||||
|
||||
info!("Profile was changed: {}", &new_profile);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pstate_for_fan_mode(
|
||||
&self,
|
||||
mode: FanLevel,
|
||||
pub(super) fn set_fan_mode(&mut self, preset: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
let mode = config.active_profile.clone();
|
||||
let mut fan_ctrl = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
config.read();
|
||||
let mut mode_config = config
|
||||
.power_profiles
|
||||
.get_mut(&mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.clone()))?;
|
||||
config.power_profile = preset;
|
||||
mode_config.fan_preset = preset;
|
||||
config.write();
|
||||
fan_ctrl
|
||||
.write_all(format!("{}\n", preset).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
info!("Fan mode set to: {:?}", FanLevel::from(preset));
|
||||
self.set_pstate_for_fan_mode(&mode, config)?;
|
||||
self.set_fan_curve_for_fan_mode(&mode, config)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_profile_event(
|
||||
&mut self,
|
||||
event: &ProfileEvent,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
) -> Result<(), RogError> {
|
||||
match event {
|
||||
ProfileEvent::Toggle => self.do_next_profile(config)?,
|
||||
ProfileEvent::ChangeMode(mode) => {
|
||||
self.set_fan_mode(*mode, config)?;
|
||||
}
|
||||
ProfileEvent::Cli(command) => {
|
||||
let profile_key = match command.profile.as_ref() {
|
||||
Some(k) => k.clone(),
|
||||
None => config.active_profile.clone(),
|
||||
};
|
||||
|
||||
let mut profile = if command.create {
|
||||
config
|
||||
.power_profiles
|
||||
.entry(profile_key.clone())
|
||||
.or_insert_with(Profile::default)
|
||||
} else {
|
||||
config
|
||||
.power_profiles
|
||||
.get_mut(&profile_key)
|
||||
.ok_or_else(|| RogError::MissingProfile(profile_key.clone()))?
|
||||
};
|
||||
|
||||
if command.turbo.is_some() {
|
||||
profile.turbo = command.turbo.unwrap();
|
||||
}
|
||||
if let Some(min_perc) = command.min_percentage {
|
||||
profile.min_percentage = min_perc;
|
||||
}
|
||||
if let Some(max_perc) = command.max_percentage {
|
||||
profile.max_percentage = max_perc;
|
||||
}
|
||||
if let Some(ref preset) = command.preset {
|
||||
profile.fan_preset = preset.into();
|
||||
}
|
||||
if let Some(ref curve) = command.curve {
|
||||
profile.fan_curve = Some(curve.clone());
|
||||
}
|
||||
|
||||
self.set(&profile_key, config)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, profile: &str, config: &mut Config) -> Result<(), RogError> {
|
||||
let mode_config = config
|
||||
.power_profiles
|
||||
.get(profile)
|
||||
.ok_or_else(|| RogError::MissingProfile(profile.into()))?;
|
||||
let mut fan_ctrl = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
fan_ctrl
|
||||
.write_all(format!("{}\n", mode_config.fan_preset).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
config.power_profile = mode_config.fan_preset;
|
||||
|
||||
self.set_pstate_for_fan_mode(profile, config)?;
|
||||
self.set_fan_curve_for_fan_mode(profile, config)?;
|
||||
|
||||
config.active_profile = profile.into();
|
||||
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pstate_for_fan_mode(&self, mode: &str, config: &mut Config) -> Result<(), RogError> {
|
||||
info!("Setting pstate");
|
||||
let mode_config = config
|
||||
.power_profiles
|
||||
.get(mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.into()))?;
|
||||
|
||||
// Set CPU pstate
|
||||
if let Ok(pstate) = intel_pstate::PState::new() {
|
||||
match mode {
|
||||
FanLevel::Normal => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.normal.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.normal.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.normal.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.normal.min_percentage,
|
||||
config.power_profiles.normal.max_percentage,
|
||||
!config.power_profiles.normal.no_turbo
|
||||
);
|
||||
}
|
||||
FanLevel::Boost => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.boost.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.boost.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.boost.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.boost.min_percentage,
|
||||
config.power_profiles.boost.max_percentage,
|
||||
!config.power_profiles.boost.no_turbo
|
||||
);
|
||||
}
|
||||
FanLevel::Silent => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.silent.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.silent.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.silent.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.silent.min_percentage,
|
||||
config.power_profiles.silent.max_percentage,
|
||||
!config.power_profiles.silent.no_turbo
|
||||
);
|
||||
}
|
||||
}
|
||||
pstate.set_min_perf_pct(mode_config.min_percentage)?;
|
||||
pstate.set_max_perf_pct(mode_config.max_percentage)?;
|
||||
pstate.set_no_turbo(!mode_config.turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {}%, max: {}%, turbo: {}",
|
||||
mode_config.min_percentage, mode_config.max_percentage, mode_config.turbo
|
||||
);
|
||||
} else {
|
||||
info!("Setting pstate for AMD CPU");
|
||||
// must be AMD CPU
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(AMD_BOOST_PATH)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to open AMD boost: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
match mode {
|
||||
FanLevel::Normal => {
|
||||
let boost = if config.power_profiles.normal.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
}; // opposite of Intel
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
FanLevel::Boost => {
|
||||
let boost = if config.power_profiles.boost.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
};
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
FanLevel::Silent => {
|
||||
let boost = if config.power_profiles.silent.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
};
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
|
||||
let boost = if mode_config.turbo { "1" } else { "0" }; // opposite of Intel
|
||||
file.write_all(boost.as_bytes())
|
||||
.map_err(|err| RogError::Write(AMD_BOOST_PATH.into(), err))?;
|
||||
info!("AMD CPU Turbo: {}", boost);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_fan_curve_for_fan_mode(&self, mode: &str, config: &Config) -> Result<(), RogError> {
|
||||
let mode_config = &config
|
||||
.power_profiles
|
||||
.get(mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.into()))?;
|
||||
|
||||
if let Some(ref curve) = mode_config.fan_curve {
|
||||
use rog_fan_curve::{Board, Fan};
|
||||
if let Some(board) = Board::from_board_name() {
|
||||
curve.apply(board, Fan::Cpu)?;
|
||||
curve.apply(board, Fan::Gpu)?;
|
||||
} else {
|
||||
warn!("Fan curve unsupported on this board.")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,153 +2,310 @@
|
||||
static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
use crate::{config::Config, error::RogError};
|
||||
use asus_nb::{
|
||||
aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH,
|
||||
LED_MSG_LEN,
|
||||
};
|
||||
use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal};
|
||||
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
||||
|
||||
use crate::{config::Config, error::RogError, laptops::HELP_ADDRESS};
|
||||
use asus_nb::{aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, LED_MSG_LEN};
|
||||
use log::{info, warn};
|
||||
use std::error::Error;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::Mutex;
|
||||
use std::{convert::TryInto, path::Path};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
pub struct CtrlKbdBacklight {
|
||||
led_node: String,
|
||||
kbd_node: String,
|
||||
bright_node: String,
|
||||
led_node: Option<String>,
|
||||
#[allow(dead_code)]
|
||||
kbd_node: Option<String>,
|
||||
pub bright_node: String,
|
||||
supported_modes: Vec<u8>,
|
||||
flip_effect_write: bool,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use async_trait::async_trait;
|
||||
pub struct DbusKbdBacklight {
|
||||
inner: Arc<Mutex<CtrlKbdBacklight>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlKbdBacklight {
|
||||
type A = AuraModes;
|
||||
impl DbusKbdBacklight {
|
||||
pub fn new(inner: Arc<Mutex<CtrlKbdBacklight>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
connection: Option<Arc<SyncConnection>>,
|
||||
signal: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
let gate1 = Arc::new(Mutex::new(self));
|
||||
let gate2 = gate1.clone();
|
||||
trait Dbus {
|
||||
fn set_led(&mut self, data: String);
|
||||
fn ledmode(&self) -> String;
|
||||
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
let config1 = config.clone();
|
||||
vec![
|
||||
tokio::spawn(async move {
|
||||
while let Some(command) = recv.recv().await {
|
||||
let mut config = config1.lock().await;
|
||||
let mut lock = gate1.lock().await;
|
||||
match &command {
|
||||
impl crate::ZbusAdd for DbusKbdBacklight {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Led".try_into().unwrap(), self)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl DbusKbdBacklight {
|
||||
fn set_led_mode(&mut self, data: String) {
|
||||
if let Ok(data) = serde_json::from_str(&data) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
match &data {
|
||||
AuraModes::PerKey(_) => {
|
||||
lock.do_command(command, &mut config)
|
||||
.await
|
||||
ctrl.do_command(data, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
_ => {
|
||||
let json = serde_json::to_string(&command).unwrap();
|
||||
lock.do_command(command, &mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
connection
|
||||
.as_ref()
|
||||
.expect("LED Controller must have DBUS connection")
|
||||
.send(
|
||||
signal
|
||||
.as_ref()
|
||||
.expect("LED Controller must have DBUS signal")
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(json),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
if let Ok(json) = serde_json::to_string(&data) {
|
||||
ctrl.do_command(data, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_led(&json).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::delay_for(std::time::Duration::from_millis(100)).await;
|
||||
let mut lock = gate2.lock().await;
|
||||
let mut config = config.lock().await;
|
||||
lock.let_bright_check_change(&mut config)
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
} else {
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
}
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
// set current mode (if any)
|
||||
if self.supported_modes.len() > 1 {
|
||||
if self.supported_modes.contains(&config.kbd_backlight_mode) {
|
||||
let mode = config
|
||||
.get_led_mode_data(config.kbd_backlight_mode)
|
||||
.ok_or(RogError::NotSupported)?
|
||||
.to_owned();
|
||||
self.write_mode(&mode).await?;
|
||||
info!("Reloaded last used mode");
|
||||
} else {
|
||||
warn!(
|
||||
"An unsupported mode was set: {}, reset to first mode available",
|
||||
<&str>::from(&<AuraModes>::from(config.kbd_backlight_mode))
|
||||
);
|
||||
for (idx, mode) in config.kbd_backlight_modes.iter_mut().enumerate() {
|
||||
if !self.supported_modes.contains(&mode.into()) {
|
||||
config.kbd_backlight_modes.remove(idx);
|
||||
config.write();
|
||||
break;
|
||||
fn next_led_mode(&self) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
ctrl.toggle_mode(false, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
|
||||
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
|
||||
if let Ok(json) = serde_json::to_string(&mode) {
|
||||
self.notify_led(&json)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
config.kbd_backlight_mode = self.supported_modes[0];
|
||||
// TODO: do a recursive call with a boxed dyn future later
|
||||
let mode = config
|
||||
.get_led_mode_data(config.kbd_backlight_mode)
|
||||
.ok_or(RogError::NotSupported)?
|
||||
.to_owned();
|
||||
self.write_mode(&mode).await?;
|
||||
info!("Reloaded last used mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reload brightness
|
||||
let bright = config.kbd_led_brightness;
|
||||
let bytes = aura_brightness_bytes(bright);
|
||||
self.write_bytes(&bytes).await?;
|
||||
info!("Reloaded last used brightness");
|
||||
fn prev_led_mode(&self) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
ctrl.toggle_mode(true, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
|
||||
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
|
||||
if let Ok(json) = serde_json::to_string(&mode) {
|
||||
self.notify_led(&json)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current mode data
|
||||
fn led_mode(&self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(cfg) = ctrl.config.clone().try_lock() {
|
||||
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
|
||||
if let Ok(json) = serde_json::to_string(&mode) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
"SetKeyBacklight could not deserialise".to_string()
|
||||
}
|
||||
|
||||
/// Return a list of available modes
|
||||
fn led_modes(&self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(cfg) = ctrl.config.clone().try_lock() {
|
||||
if let Ok(json) = serde_json::to_string(&cfg.kbd_backlight_modes) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
"SetKeyBacklight could not deserialise".to_string()
|
||||
}
|
||||
|
||||
/// Return the current LED brightness
|
||||
fn led_brightness(&self) -> i8 {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(cfg) = ctrl.config.clone().try_lock() {
|
||||
return cfg.kbd_led_brightness as i8;
|
||||
}
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlKbdBacklight {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
// set current mode (if any)
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
if self.supported_modes.len() > 1 {
|
||||
if self.supported_modes.contains(&config.kbd_backlight_mode) {
|
||||
let mode = config
|
||||
.get_led_mode_data(config.kbd_backlight_mode)
|
||||
.ok_or(RogError::NotSupported)?
|
||||
.to_owned();
|
||||
self.write_mode(&mode)?;
|
||||
info!("Reloaded last used mode");
|
||||
} else {
|
||||
warn!(
|
||||
"An unsupported mode was set: {}, reset to first mode available",
|
||||
<&str>::from(&<AuraModes>::from(config.kbd_backlight_mode))
|
||||
);
|
||||
for (idx, mode) in config.kbd_backlight_modes.iter_mut().enumerate() {
|
||||
if !self.supported_modes.contains(&mode.into()) {
|
||||
config.kbd_backlight_modes.remove(idx);
|
||||
config.write();
|
||||
break;
|
||||
}
|
||||
}
|
||||
config.kbd_backlight_mode = self.supported_modes[0];
|
||||
// TODO: do a recursive call with a boxed dyn future later
|
||||
let mode = config
|
||||
.get_led_mode_data(config.kbd_backlight_mode)
|
||||
.ok_or(RogError::NotSupported)?
|
||||
.to_owned();
|
||||
self.write_mode(&mode)?;
|
||||
info!("Reloaded last used mode");
|
||||
}
|
||||
}
|
||||
|
||||
// Reload brightness
|
||||
let bright = config.kbd_led_brightness;
|
||||
let bytes = aura_brightness_bytes(bright);
|
||||
self.write_bytes(&bytes)?;
|
||||
info!("Reloaded last used brightness");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CtrlTask for CtrlKbdBacklight {
|
||||
fn do_task(&mut self) -> Result<(), RogError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&self.bright_node)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
||||
}
|
||||
_ => RogError::Path((&self.bright_node).into(), err),
|
||||
})?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
if config.kbd_led_brightness != num as u8 {
|
||||
config.read();
|
||||
config.kbd_led_brightness = num as u8;
|
||||
config.write();
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Err(RogError::ParseLED)
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlKbdBacklight {
|
||||
#[inline]
|
||||
pub fn new(id_product: &str, supported_modes: Vec<u8>) -> Result<Self, std::io::Error> {
|
||||
Ok(CtrlKbdBacklight {
|
||||
led_node: Self::scan_led_node(id_product)?,
|
||||
kbd_node: Self::scan_kbd_node(id_product)?,
|
||||
// brightness node path should always be constant but this *might* change?
|
||||
bright_node: "/sys/class/leds/asus::kbd_backlight/brightness".to_string(),
|
||||
pub fn new(
|
||||
id_product: &str,
|
||||
condev_iface: Option<&String>,
|
||||
supported_modes: Vec<u8>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
) -> Result<Self, RogError> {
|
||||
// TODO: return error if *all* nodes are None
|
||||
let ctrl = CtrlKbdBacklight {
|
||||
// Using `ok` here so we can continue without keyboard features but
|
||||
// still get brightness control at least... maybe...
|
||||
led_node: Self::get_node_failover(id_product, None, Self::scan_led_node).ok(),
|
||||
kbd_node: Self::get_node_failover(id_product, condev_iface, Self::scan_kbd_node).ok(),
|
||||
// TODO: Check for existance
|
||||
bright_node: Self::get_kbd_bright_path()?.to_owned(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
})
|
||||
config,
|
||||
};
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
fn scan_led_node(id_product: &str) -> Result<String, std::io::Error> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
enumerator.match_subsystem("hidraw")?;
|
||||
fn get_kbd_bright_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(KBD_BRIGHT_PATH).exists() {
|
||||
Ok(KBD_BRIGHT_PATH)
|
||||
} else {
|
||||
Err(RogError::MissingFunction(
|
||||
"Keyboard features missing, you may require a v5.11 series kernel or newer".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
for device in enumerator.scan_devices()? {
|
||||
if let Some(parent) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
|
||||
if parent.attribute_value("idProduct").unwrap() == id_product {
|
||||
// && device.parent().unwrap().sysnum().unwrap() == 3
|
||||
fn get_node_failover(
|
||||
id_product: &str,
|
||||
iface: Option<&String>,
|
||||
fun: fn(&str, Option<&String>) -> Result<String, RogError>,
|
||||
) -> Result<String, RogError> {
|
||||
// We do three tries here just to be certain that we avoid systemd unit
|
||||
// load order issues
|
||||
for n in 0..=2 {
|
||||
// 0,1,2 inclusive
|
||||
match fun(id_product, iface) {
|
||||
Ok(o) => return Ok(o),
|
||||
Err(e) => {
|
||||
if n == 2 {
|
||||
warn!("Looking for node: {}", e.to_string());
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shouldn't be possible to reach this...
|
||||
Err(RogError::NotFound(format!("{}, {:?}", id_product, iface)))
|
||||
}
|
||||
|
||||
fn scan_led_node(id_product: &str, _: Option<&String>) -> Result<String, RogError> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
if let Some(parent) = device
|
||||
.parent_with_subsystem_devtype("usb", "usb_device")
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
|
||||
})?
|
||||
{
|
||||
if parent
|
||||
.attribute_value("idProduct")
|
||||
.ok_or(RogError::NotFound("LED idProduct".into()))?
|
||||
== id_product
|
||||
{
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
info!("Using device at: {:?} for LED control", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
@@ -156,82 +313,84 @@ impl CtrlKbdBacklight {
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS LED device node not found",
|
||||
);
|
||||
Err(err)
|
||||
warn!("Did not find a hidraw node for LED control, your device may be unsupported or require a kernel patch, see: {}", HELP_ADDRESS);
|
||||
Err(RogError::MissingFunction(
|
||||
"ASUS LED device node not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_kbd_node(id_product: &str) -> Result<String, std::io::Error> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
enumerator.match_subsystem("input")?;
|
||||
enumerator.match_property("ID_MODEL_ID", id_product)?;
|
||||
fn scan_kbd_node(id_product: &str, iface: Option<&String>) -> Result<String, RogError> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("input").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
enumerator
|
||||
.match_property("ID_MODEL_ID", id_product)
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_property failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices()? {
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
err
|
||||
})
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("scan_devices failed".into(), err)
|
||||
})?
|
||||
{
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
if let Some(inum) = device.property_value("ID_USB_INTERFACE_NUM") {
|
||||
if inum == "02" {
|
||||
info!("Using device at: {:?} for keyboard polling", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
if let Some(iface) = iface {
|
||||
if inum == iface.as_str() {
|
||||
info!("Using device at: {:?} for keyboard polling", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS N-Key Consumer Device node not found",
|
||||
);
|
||||
Err(err)
|
||||
|
||||
warn!("Did not find keyboard consumer device node, if expected functions are missing please file an issue at {}", HELP_ADDRESS);
|
||||
Err(RogError::MissingFunction(
|
||||
"ASUS keyboard 'Consumer Device' node not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn let_bright_check_change(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = OpenOptions::new().read(true).open(&self.bright_node)?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if config.kbd_led_brightness != num as u8 {
|
||||
config.read();
|
||||
config.kbd_led_brightness = num as u8;
|
||||
config.write();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"LED brightness could not be parsed",
|
||||
);
|
||||
Err(Box::new(err))
|
||||
}
|
||||
|
||||
pub async fn do_command(
|
||||
&mut self,
|
||||
mode: AuraModes,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
self.set_and_save(mode, config).await
|
||||
pub fn do_command(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
|
||||
self.set_and_save(mode, config)
|
||||
}
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
#[inline]
|
||||
async fn write_bytes(&self, message: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||
if let Ok(mut file) = OpenOptions::new().write(true).open(&self.led_node) {
|
||||
file.write_all(message).unwrap();
|
||||
return Ok(());
|
||||
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
if let Some(led_node) = &self.led_node {
|
||||
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
|
||||
return file
|
||||
.write_all(message)
|
||||
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||
}
|
||||
}
|
||||
Err(Box::new(RogError::NotSupported))
|
||||
Err(RogError::NotSupported)
|
||||
}
|
||||
|
||||
/// Write an effect block
|
||||
#[inline]
|
||||
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), Box<dyn Error>> {
|
||||
fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
||||
if self.flip_effect_write {
|
||||
for row in effect.iter().rev() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
} else {
|
||||
for row in effect.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
@@ -242,15 +401,11 @@ impl CtrlKbdBacklight {
|
||||
///
|
||||
/// This needs to be universal so that settings applied by dbus stick
|
||||
#[inline]
|
||||
async fn set_and_save(
|
||||
&mut self,
|
||||
mode: AuraModes,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
fn set_and_save(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
|
||||
match mode {
|
||||
AuraModes::LedBrightness(n) => {
|
||||
let bytes: [u8; LED_MSG_LEN] = (&mode).into();
|
||||
self.write_bytes(&bytes).await?;
|
||||
self.write_bytes(&bytes)?;
|
||||
config.read();
|
||||
config.kbd_led_brightness = n;
|
||||
config.write();
|
||||
@@ -259,15 +414,15 @@ impl CtrlKbdBacklight {
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes).await?;
|
||||
self.write_bytes(&bytes)?;
|
||||
} else {
|
||||
self.write_effect(&v).await?;
|
||||
self.write_effect(&v)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
config.read();
|
||||
let mode_num: u8 = u8::from(&mode);
|
||||
self.write_mode(&mode).await?;
|
||||
self.write_mode(&mode)?;
|
||||
config.kbd_backlight_mode = mode_num;
|
||||
config.set_mode_data(mode);
|
||||
config.write();
|
||||
@@ -277,14 +432,45 @@ impl CtrlKbdBacklight {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), Box<dyn Error>> {
|
||||
fn toggle_mode(&mut self, reverse: bool, config: &mut Config) -> Result<(), RogError> {
|
||||
let current = config.kbd_backlight_mode;
|
||||
if let Some(idx) = self.supported_modes.iter().position(|v| *v == current) {
|
||||
let mut idx = idx;
|
||||
// goes past end of array
|
||||
if reverse {
|
||||
if idx == 0 {
|
||||
idx = self.supported_modes.len() - 1;
|
||||
} else {
|
||||
idx -= 1;
|
||||
}
|
||||
} else {
|
||||
idx += 1;
|
||||
if idx == self.supported_modes.len() {
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
let next = self.supported_modes[idx];
|
||||
|
||||
config.read();
|
||||
if let Some(data) = config.get_led_mode_data(next) {
|
||||
self.write_mode(&data)?;
|
||||
config.kbd_backlight_mode = next;
|
||||
}
|
||||
config.write();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_mode(&mut self, mode: &AuraModes) -> Result<(), RogError> {
|
||||
match mode {
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes).await?;
|
||||
self.write_bytes(&bytes)?;
|
||||
} else {
|
||||
self.write_effect(v).await?;
|
||||
self.write_effect(v)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -294,20 +480,20 @@ impl CtrlKbdBacklight {
|
||||
if self.supported_modes.contains(&mode_num) {
|
||||
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
|
||||
for array in bytes.iter() {
|
||||
self.write_bytes(array).await?;
|
||||
self.write_bytes(array)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if self.supported_modes.contains(&mode_num) {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
self.write_bytes(&bytes).await?;
|
||||
self.write_bytes(&bytes)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.write_bytes(&LED_SET).await?;
|
||||
self.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY).await?;
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
use daemon::{
|
||||
config::Config, ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge,
|
||||
ctrl_fan_cpu::CtrlFanAndCPU, ctrl_leds::CtrlKbdBacklight, dbus::dbus_create_tree,
|
||||
laptops::match_laptop,
|
||||
};
|
||||
use ctrl_gfx::ctrl_gfx::CtrlGraphics;
|
||||
use daemon::config::Config;
|
||||
use daemon::ctrl_anime::CtrlAnimeDisplay;
|
||||
use daemon::ctrl_charge::CtrlCharge;
|
||||
use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu};
|
||||
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
|
||||
use daemon::laptops::match_laptop;
|
||||
|
||||
use dbus::{
|
||||
channel::Sender,
|
||||
nonblock::{Process, SyncConnection},
|
||||
tree::Signal,
|
||||
};
|
||||
use dbus_tokio::connection;
|
||||
|
||||
use asus_nb::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||
use daemon::Controller;
|
||||
use asus_nb::DBUS_NAME;
|
||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||
use log::LevelFilter;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::convert::Into;
|
||||
use std::convert::TryInto;
|
||||
use zbus::fdo;
|
||||
use zbus::Connection;
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.target(env_logger::Target::Stdout)
|
||||
@@ -30,7 +29,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.init();
|
||||
|
||||
info!("Version: {}", daemon::VERSION);
|
||||
start_daemon().await?;
|
||||
start_daemon()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -43,211 +42,113 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// as fast as 1ms per row of the matrix inside it. (10ms total time)
|
||||
//
|
||||
// DBUS processing takes 6ms if not tokiod
|
||||
pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
let laptop = match_laptop();
|
||||
let mut config = if let Some(laptop) = laptop.as_ref() {
|
||||
let config = if let Some(laptop) = laptop.as_ref() {
|
||||
Config::default().load(laptop.supported_modes())
|
||||
} else {
|
||||
Config::default().load(&[])
|
||||
};
|
||||
|
||||
let mut led_control = if let Some(laptop) = laptop {
|
||||
CtrlKbdBacklight::new(laptop.usb_product(), laptop.supported_modes().to_owned())
|
||||
.map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut charge_control = CtrlCharge::new().map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
let mut fan_control = CtrlFanAndCPU::new().map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
// Reload settings
|
||||
if let Some(ctrl) = fan_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Fan mode: {}", err));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = charge_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = led_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Reload settings: {}", err));
|
||||
}
|
||||
|
||||
let (resource, connection) = connection::new_system_sync()?;
|
||||
tokio::spawn(async {
|
||||
let err = resource.await;
|
||||
panic!("Lost connection to D-Bus: {}", err);
|
||||
});
|
||||
|
||||
connection
|
||||
.request_name(DBUS_NAME, false, true, true)
|
||||
.await?;
|
||||
let connection = Connection::new_system()?;
|
||||
fdo::DBusProxy::new(&connection)?
|
||||
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
||||
let mut object_server = zbus::ObjectServer::new(&connection);
|
||||
|
||||
let enable_gfx_switching = config.gfx_managed;
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
let (
|
||||
tree,
|
||||
aura_command_recv,
|
||||
animatrix_recv,
|
||||
fan_mode_recv,
|
||||
charge_limit_recv,
|
||||
led_changed_signal,
|
||||
fanmode_signal,
|
||||
charge_limit_signal,
|
||||
) = dbus_create_tree(config.clone());
|
||||
|
||||
// We add the tree to the connection so that incoming method calls will be handled.
|
||||
tree.start_receive_send(&*connection);
|
||||
|
||||
// Send boot signals
|
||||
send_boot_signals(
|
||||
connection.clone(),
|
||||
config.clone(),
|
||||
fanmode_signal.clone(),
|
||||
charge_limit_signal.clone(),
|
||||
led_changed_signal.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// For helping with processing signals
|
||||
start_signal_task(
|
||||
connection.clone(),
|
||||
config.clone(),
|
||||
fanmode_signal,
|
||||
charge_limit_signal,
|
||||
);
|
||||
|
||||
// Begin all tasks
|
||||
let mut handles = Vec::new();
|
||||
if let Ok(ctrl) = CtrlAnimeDisplay::new() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), animatrix_recv, None, None));
|
||||
match CtrlCharge::new(config.clone()) {
|
||||
Ok(mut ctrl) => {
|
||||
// Do a reload of any settings
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||
// Then register to dbus server
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("charge_control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ctrl) = fan_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), fan_mode_recv, None, None));
|
||||
match CtrlAnimeDisplay::new() {
|
||||
Ok(ctrl) => {
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("AniMe control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ctrl) = charge_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), charge_limit_recv, None, None));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = led_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(
|
||||
config.clone(),
|
||||
aura_command_recv,
|
||||
Some(connection.clone()),
|
||||
Some(led_changed_signal),
|
||||
));
|
||||
}
|
||||
|
||||
connection.process_all();
|
||||
for handle in handles {
|
||||
handle.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Move these in to the controllers tasks
|
||||
fn start_signal_task(
|
||||
connection: Arc<SyncConnection>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
fanmode_signal: Arc<Signal<()>>,
|
||||
charge_limit_signal: Arc<Signal<()>>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
// Some small things we need to track, without passing all sorts of stuff around
|
||||
let mut last_fan_mode = config.lock().await.power_profile;
|
||||
let mut last_charge_limit = config.lock().await.bat_charge_limit;
|
||||
loop {
|
||||
// Use tokio sleep to not hold up other threads
|
||||
tokio::time::delay_for(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
let config = config.lock().await;
|
||||
if config.power_profile != last_fan_mode {
|
||||
last_fan_mode = config.power_profile;
|
||||
connection
|
||||
.send(
|
||||
fanmode_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(last_fan_mode),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
if enable_gfx_switching {
|
||||
match CtrlGraphics::new() {
|
||||
Ok(mut ctrl) => {
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
|
||||
if config.bat_charge_limit != last_charge_limit {
|
||||
last_charge_limit = config.bat_charge_limit;
|
||||
connection
|
||||
.send(
|
||||
charge_limit_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(last_charge_limit),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
Err(err) => {
|
||||
error!("Gfx control: {}", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn send_boot_signals(
|
||||
connection: Arc<SyncConnection>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
fanmode_signal: Arc<Signal<()>>,
|
||||
charge_limit_signal: Arc<Signal<()>>,
|
||||
led_changed_signal: Arc<Signal<()>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let config = config.lock().await;
|
||||
|
||||
if let Some(data) = config.get_led_mode_data(config.kbd_backlight_mode) {
|
||||
connection
|
||||
.send(
|
||||
led_changed_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(serde_json::to_string(data)?),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
}
|
||||
|
||||
connection
|
||||
.send(
|
||||
fanmode_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(config.power_profile),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
// Collect tasks for task thread
|
||||
let mut tasks: Vec<Arc<Mutex<dyn CtrlTask + Send>>> = Vec::new();
|
||||
|
||||
connection
|
||||
.send(
|
||||
charge_limit_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(config.bat_charge_limit),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
if let Ok(mut ctrl) = CtrlFanAndCPU::new(config.clone()).map_err(|err| {
|
||||
error!("Profile control: {}", err);
|
||||
}) {
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Profile control: {}", err));
|
||||
let tmp = Arc::new(Mutex::new(ctrl));
|
||||
DbusFanAndCpu::new(tmp.clone()).add_to_server(&mut object_server);
|
||||
tasks.push(tmp);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
if let Some(laptop) = laptop {
|
||||
if let Ok(ctrl) = CtrlKbdBacklight::new(
|
||||
laptop.usb_product(),
|
||||
laptop.condev_iface(),
|
||||
laptop.supported_modes().to_owned(),
|
||||
config,
|
||||
)
|
||||
.map_err(|err| {
|
||||
error!("Keyboard control: {}", err);
|
||||
}) {
|
||||
let tmp = Arc::new(Mutex::new(ctrl));
|
||||
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
|
||||
tasks.push(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement messaging between threads to check fails
|
||||
// These tasks generally read a sys path or file to check for a
|
||||
// change
|
||||
let _handle = std::thread::Builder::new()
|
||||
.name("asusd watch".to_string())
|
||||
.spawn(move || loop {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
||||
for ctrl in tasks.iter() {
|
||||
if let Ok(mut lock) = ctrl.try_lock() {
|
||||
lock.do_task()
|
||||
.map_err(|err| {
|
||||
warn!("do_task error: {}", err);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
object_server.with(&"/org/asuslinux/Charge".try_into()?, |obj: &CtrlCharge| {
|
||||
let x = obj.limit();
|
||||
obj.notify_charge(x as u8)
|
||||
})?;
|
||||
|
||||
loop {
|
||||
if let Err(err) = object_server.try_handle_next() {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
use crate::config::Config;
|
||||
use asus_nb::{aura_modes::AuraModes, DBUS_IFACE, DBUS_PATH};
|
||||
use dbus::tree::{Factory, MTSync, Method, MethodErr, Signal, Tree};
|
||||
use log::warn;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
fn set_keyboard_backlight(sender: Mutex<Sender<AuraModes>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetKeyBacklight", (), {
|
||||
move |m| {
|
||||
let json: &str = m.msg.read1()?;
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
if let Ok(data) = serde_json::from_str(json) {
|
||||
lock.try_send(data).unwrap_or_else(|err| {
|
||||
warn!("SetKeyBacklight over mpsc failed: {}", err)
|
||||
});
|
||||
} else {
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
}
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<&str, _>("json")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn get_keyboard_backlight(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetKeyBacklight", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
for mode in &lock.kbd_backlight_modes {
|
||||
if lock.kbd_backlight_mode == <u8>::from(mode) {
|
||||
let mode = serde_json::to_string(&mode).unwrap();
|
||||
let mret = m.msg.method_return().append1(mode);
|
||||
return Ok(vec![mret]);
|
||||
}
|
||||
}
|
||||
Err(MethodErr::failed(
|
||||
"Keyboard LED mode set to an invalid mode",
|
||||
))
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<&str, _>("json")
|
||||
}
|
||||
|
||||
fn get_keyboard_backlight_modes(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetKeyBacklightModes", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mode = serde_json::to_string(&lock.kbd_backlight_modes).unwrap();
|
||||
let mret = m.msg.method_return().append1(mode);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<&str, _>("json")
|
||||
}
|
||||
|
||||
fn set_animatrix(
|
||||
sender: Mutex<Sender<Vec<Vec<u8>>>>, // need mutex only to get interior mutability in MTSync
|
||||
) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("AnimatrixWrite", (), {
|
||||
move |m| {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte_array: Vec<Vec<u8>> = vec![iter.read()?, iter.read()?];
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
// Ignore errors if the channel is already full
|
||||
lock.try_send(byte_array).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<Vec<u8>, _>("bytearray1")
|
||||
.inarg::<Vec<u8>, _>("bytearray2")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn set_fan_mode(sender: Mutex<Sender<u8>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetFanMode", (), {
|
||||
move |m| {
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte: u8 = iter.read()?;
|
||||
lock.try_send(byte).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<u8, _>("mode")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn get_fan_mode(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetFanMode", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mret = m.msg.method_return().append1(lock.power_profile);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<u8, _>("mode")
|
||||
}
|
||||
|
||||
fn get_charge_limit(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetChargeLimit", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mret = m.msg.method_return().append1(lock.bat_charge_limit);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<u8, _>("limit")
|
||||
}
|
||||
|
||||
fn set_charge_limit(sender: Mutex<Sender<u8>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetChargeLimit", (), {
|
||||
move |m| {
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte: u8 = iter.read()?;
|
||||
lock.try_send(byte).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<u8, _>("limit")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn dbus_create_tree(
|
||||
config: Arc<Mutex<Config>>,
|
||||
) -> (
|
||||
Tree<MTSync, ()>,
|
||||
Receiver<AuraModes>,
|
||||
Receiver<Vec<Vec<u8>>>,
|
||||
Receiver<u8>,
|
||||
Receiver<u8>,
|
||||
Arc<Signal<()>>,
|
||||
Arc<Signal<()>>,
|
||||
Arc<Signal<()>>,
|
||||
) {
|
||||
let (aura_command_send, aura_command_recv) = channel::<AuraModes>(1);
|
||||
let (animatrix_send, animatrix_recv) = channel::<Vec<Vec<u8>>>(1);
|
||||
let (fan_mode_send, fan_mode_recv) = channel::<u8>(1);
|
||||
let (charge_send, charge_recv) = channel::<u8>(1);
|
||||
|
||||
let factory = Factory::new_sync::<()>();
|
||||
|
||||
let key_backlight_changed = Arc::new(
|
||||
factory
|
||||
.signal("KeyBacklightChanged", ())
|
||||
.sarg::<&str, _>("json"),
|
||||
);
|
||||
let chrg_limit_changed = Arc::new(
|
||||
factory
|
||||
.signal("ChargeLimitChanged", ())
|
||||
.sarg::<u8, _>("limit"),
|
||||
);
|
||||
let fanmode_changed = Arc::new(factory.signal("FanModeChanged", ()).sarg::<u8, _>("mode"));
|
||||
|
||||
let tree = factory
|
||||
.tree(())
|
||||
.add(
|
||||
factory.object_path(DBUS_PATH, ()).introspectable().add(
|
||||
factory
|
||||
.interface(DBUS_IFACE, ())
|
||||
.add_m(set_keyboard_backlight(Mutex::new(aura_command_send)))
|
||||
.add_m(set_animatrix(Mutex::new(animatrix_send)))
|
||||
.add_m(set_fan_mode(Mutex::new(fan_mode_send)))
|
||||
.add_m(set_charge_limit(Mutex::new(charge_send)))
|
||||
.add_m(get_fan_mode(config.clone()))
|
||||
.add_m(get_charge_limit(config.clone()))
|
||||
.add_m(get_keyboard_backlight(config.clone()))
|
||||
.add_m(get_keyboard_backlight_modes(config))
|
||||
.add_s(key_backlight_changed.clone())
|
||||
.add_s(fanmode_changed.clone())
|
||||
.add_s(chrg_limit_changed.clone()),
|
||||
),
|
||||
)
|
||||
.add(factory.object_path("/", ()).introspectable());
|
||||
(
|
||||
tree,
|
||||
aura_command_recv,
|
||||
animatrix_recv,
|
||||
fan_mode_recv,
|
||||
charge_recv,
|
||||
key_backlight_changed,
|
||||
fanmode_changed,
|
||||
chrg_limit_changed,
|
||||
)
|
||||
}
|
||||
@@ -1,19 +1,60 @@
|
||||
use intel_pstate::PStateError;
|
||||
use rog_fan_curve::CurveError;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RogError {
|
||||
ParseFanLevel,
|
||||
ParseVendor,
|
||||
ParseLED,
|
||||
MissingProfile(String),
|
||||
Udev(String, std::io::Error),
|
||||
Path(String, std::io::Error),
|
||||
Read(String, std::io::Error),
|
||||
Write(String, std::io::Error),
|
||||
NotSupported,
|
||||
NotFound(String),
|
||||
IntelPstate(PStateError),
|
||||
FanCurve(CurveError),
|
||||
DoTask(String),
|
||||
MissingFunction(String),
|
||||
MissingLedBrightNode(String, std::io::Error),
|
||||
}
|
||||
|
||||
impl std::error::Error for RogError {}
|
||||
|
||||
impl fmt::Display for RogError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
RogError::ParseFanLevel => write!(f, "Parse error"),
|
||||
RogError::ParseFanLevel => write!(f, "Parse profile error"),
|
||||
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
|
||||
RogError::ParseLED => write!(f, "Parse LED error"),
|
||||
RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile),
|
||||
RogError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
|
||||
RogError::Path(path, error) => write!(f, "Path {}: {}", path, error),
|
||||
RogError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||
RogError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||
RogError::NotSupported => write!(f, "Not supported"),
|
||||
RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
|
||||
RogError::IntelPstate(err) => write!(f, "Intel pstate error: {}", err),
|
||||
RogError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
|
||||
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
|
||||
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
|
||||
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RogError {}
|
||||
|
||||
impl From<PStateError> for RogError {
|
||||
fn from(err: PStateError) -> Self {
|
||||
RogError::IntelPstate(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CurveError> for RogError {
|
||||
fn from(err: CurveError) -> Self {
|
||||
RogError::FanCurve(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ use std::io::Read;
|
||||
|
||||
pub static LEDMODE_CONFIG_PATH: &str = "/etc/asusd/asusd-ledmodes.toml";
|
||||
|
||||
static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl";
|
||||
pub static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl";
|
||||
|
||||
pub struct LaptopBase {
|
||||
usb_product: String,
|
||||
condev_iface: Option<String>, // required for finding the Consumer Device interface
|
||||
supported_modes: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -17,6 +18,9 @@ impl LaptopBase {
|
||||
pub fn usb_product(&self) -> &str {
|
||||
&self.usb_product
|
||||
}
|
||||
pub fn condev_iface(&self) -> Option<&String> {
|
||||
self.condev_iface.as_ref()
|
||||
}
|
||||
pub fn supported_modes(&self) -> &[u8] {
|
||||
&self.supported_modes
|
||||
}
|
||||
@@ -37,6 +41,7 @@ pub fn match_laptop() -> Option<LaptopBase> {
|
||||
info!("Found GL753 or similar");
|
||||
return Some(LaptopBase {
|
||||
usb_product: "1854".to_string(),
|
||||
condev_iface: None,
|
||||
supported_modes: vec![STATIC, BREATHING, STROBE],
|
||||
});
|
||||
}
|
||||
@@ -44,6 +49,11 @@ pub fn match_laptop() -> Option<LaptopBase> {
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
None
|
||||
}
|
||||
|
||||
@@ -58,6 +68,7 @@ fn select_1866_device(prod: String) -> LaptopBase {
|
||||
|
||||
let mut laptop = LaptopBase {
|
||||
usb_product: prod,
|
||||
condev_iface: Some("02".to_owned()),
|
||||
supported_modes: vec![],
|
||||
};
|
||||
|
||||
@@ -67,13 +78,6 @@ fn select_1866_device(prod: String) -> LaptopBase {
|
||||
return laptop;
|
||||
}
|
||||
}
|
||||
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
|
||||
laptop
|
||||
}
|
||||
|
||||
|
||||
@@ -10,36 +10,31 @@ pub mod ctrl_fan_cpu;
|
||||
///
|
||||
pub mod ctrl_leds;
|
||||
///
|
||||
pub mod dbus;
|
||||
/// Laptop matching to determine capabilities
|
||||
pub mod laptops;
|
||||
|
||||
mod error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crate::error::RogError;
|
||||
use config::Config;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc::Receiver, Mutex};
|
||||
use tokio::task::JoinHandle;
|
||||
use zbus::ObjectServer;
|
||||
|
||||
pub static VERSION: &str = "1.0.2";
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
pub trait Reloadable {
|
||||
fn reload(&mut self) -> Result<(), RogError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Controller {
|
||||
pub trait ZbusAdd {
|
||||
fn add_to_server(self, server: &mut ObjectServer);
|
||||
}
|
||||
|
||||
pub trait CtrlTask {
|
||||
fn do_task(&mut self) -> Result<(), RogError>;
|
||||
}
|
||||
|
||||
pub trait CtrlTaskComplex {
|
||||
type A;
|
||||
|
||||
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>>;
|
||||
|
||||
/// Spawn an infinitely running task (usually) which checks a Receiver for input,
|
||||
/// and may send a signal over dbus
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
recv: Receiver<Self::A>,
|
||||
connection: Option<Arc<SyncConnection>>,
|
||||
signal: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>>;
|
||||
fn do_task(&mut self, config: &mut Config, event: Self::A);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
use asus_nb::{
|
||||
cli_options::{LedBrightness, SetAuraBuiltin},
|
||||
anime_dbus::AniMeDbusWriter,
|
||||
cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin},
|
||||
core_dbus::AuraDbusClient,
|
||||
profile::{ProfileCommand, ProfileEvent},
|
||||
};
|
||||
use ctrl_gfx::vendors::GfxVendors;
|
||||
use daemon::ctrl_fan_cpu::FanLevel;
|
||||
use gumdrop::Options;
|
||||
use gumdrop::{Opt, Options};
|
||||
use log::LevelFilter;
|
||||
use std::io::Write;
|
||||
use std::{env::args, io::Write, process::Command};
|
||||
use yansi_term::Colour::Green;
|
||||
use yansi_term::Colour::Red;
|
||||
|
||||
#[derive(Options)]
|
||||
#[derive(Default, Options)]
|
||||
struct CLIStart {
|
||||
#[options(help = "print help message")]
|
||||
#[options(help_flag, help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "show program version number")]
|
||||
version: bool,
|
||||
@@ -20,25 +25,60 @@ struct CLIStart {
|
||||
#[options(meta = "CHRG", help = "<20-100>")]
|
||||
chg_limit: Option<u8>,
|
||||
#[options(command)]
|
||||
command: Option<Command>,
|
||||
command: Option<CliCommand>,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
enum Command {
|
||||
enum CliCommand {
|
||||
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||
LedMode(LedModeCommand),
|
||||
#[options(help = "Create and configure profiles")]
|
||||
Profile(ProfileCommand),
|
||||
#[options(help = "Set the graphics mode")]
|
||||
Graphics(GraphicsCommand),
|
||||
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
||||
AniMe(AniMeCommand),
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
struct LedModeCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(command, required)]
|
||||
#[options(help = "switch to next aura mode")]
|
||||
next_mode: bool,
|
||||
#[options(help = "switch to previous aura mode")]
|
||||
prev_mode: bool,
|
||||
#[options(command)]
|
||||
command: Option<SetAuraBuiltin>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Options)]
|
||||
struct GraphicsCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "Set graphics mode: <nvidia, hybrid, compute, integrated>")]
|
||||
mode: Option<GfxVendors>,
|
||||
#[options(help = "Get the current mode")]
|
||||
get: bool,
|
||||
#[options(help = "Get the current power status")]
|
||||
pow: bool,
|
||||
#[options(help = "Do not ask for confirmation")]
|
||||
force: bool,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
struct AniMeCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "turn on/off the panel (accept/reject write requests)")]
|
||||
turn: Option<AniMeStatusValue>,
|
||||
#[options(help = "turn on/off the panel at boot (with Asus effect)")]
|
||||
boot: Option<AniMeStatusValue>,
|
||||
#[options(command)]
|
||||
command: Option<AniMeActions>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.target(env_logger::Target::Stdout)
|
||||
@@ -46,22 +86,90 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.filter(None, LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let parsed = CLIStart::parse_args_default_or_exit();
|
||||
let mut args: Vec<String> = args().collect();
|
||||
args.remove(0);
|
||||
|
||||
let parsed: CLIStart;
|
||||
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
|
||||
match CLIStart::parse_args_default(&args) {
|
||||
Ok(p) => {
|
||||
parsed = p;
|
||||
}
|
||||
Err(err) if err.to_string() == missing_argument_k.to_string() => {
|
||||
parsed = CLIStart {
|
||||
kbd_bright: Some(LedBrightness::new(None)),
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("source {}", err);
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if parsed.help_requested() {
|
||||
// As help option don't work with `parse_args_default`
|
||||
// we will call `parse_args_default_or_exit` instead
|
||||
CLIStart::parse_args_default_or_exit();
|
||||
}
|
||||
|
||||
if parsed.version {
|
||||
println!("Version: {}", daemon::VERSION);
|
||||
}
|
||||
|
||||
let writer = AuraDbusClient::new()?;
|
||||
let anime_writer = AniMeDbusWriter::new()?;
|
||||
|
||||
if let Some(Command::LedMode(mode)) = parsed.command {
|
||||
if let Some(command) = mode.command {
|
||||
writer.write_builtin_mode(&command.into())?
|
||||
match parsed.command {
|
||||
Some(CliCommand::LedMode(mode)) => {
|
||||
if mode.next_mode && mode.prev_mode {
|
||||
println!("Please specify either next or previous")
|
||||
}
|
||||
if mode.next_mode {
|
||||
writer.next_keyboard_led_mode()?;
|
||||
} else if mode.prev_mode {
|
||||
writer.prev_keyboard_led_mode()?;
|
||||
} else if let Some(command) = mode.command {
|
||||
writer.write_builtin_mode(&command.into())?
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Profile(command)) => {
|
||||
if command.next {
|
||||
writer.next_fan_profile()?;
|
||||
} else {
|
||||
writer.write_profile_command(&ProfileEvent::Cli(command))?
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Graphics(command)) => do_gfx(command, &writer)?,
|
||||
Some(CliCommand::AniMe(anime)) => {
|
||||
if let Some(anime_turn) = anime.turn {
|
||||
anime_writer.turn_on_off(anime_turn.into())?
|
||||
}
|
||||
if let Some(anime_boot) = anime.boot {
|
||||
anime_writer.turn_boot_on_off(anime_boot.into())?
|
||||
}
|
||||
if let Some(action) = anime.command {
|
||||
match action {
|
||||
AniMeActions::Leds(anime_leds) => {
|
||||
let led_brightness = anime_leds.led_brightness();
|
||||
anime_writer.set_leds_brightness(led_brightness)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
if let Some(brightness) = parsed.kbd_bright {
|
||||
match brightness.level() {
|
||||
None => {
|
||||
let level = writer.get_led_brightness()?;
|
||||
println!("Current keyboard led brightness: {}", level.to_string());
|
||||
}
|
||||
Some(level) => writer.write_brightness(level)?,
|
||||
}
|
||||
}
|
||||
if let Some(brightness) = parsed.kbd_bright {
|
||||
writer.write_brightness(brightness.level())?;
|
||||
}
|
||||
|
||||
if let Some(fan_level) = parsed.pwr_profile {
|
||||
writer.write_fan_mode(fan_level.into())?;
|
||||
}
|
||||
@@ -70,3 +178,92 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_gfx(
|
||||
command: GraphicsCommand,
|
||||
writer: &AuraDbusClient,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mode) = command.mode {
|
||||
println!("Updating settings, please wait...");
|
||||
println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`");
|
||||
|
||||
writer.write_gfx_mode(mode)?;
|
||||
let res = writer.wait_gfx_changed()?;
|
||||
match res.as_str() {
|
||||
"reboot" => {
|
||||
println!(
|
||||
"{}",
|
||||
Green.paint("\nGraphics vendor mode changed successfully\n"),
|
||||
);
|
||||
do_gfx_action(
|
||||
command.force,
|
||||
Command::new("systemctl").arg("reboot").arg("-i"),
|
||||
"Reboot Linux PC",
|
||||
"Please reboot when ready",
|
||||
)?;
|
||||
}
|
||||
"restartx" => {
|
||||
println!(
|
||||
"{}",
|
||||
Green.paint("\nGraphics vendor mode changed successfully\n")
|
||||
);
|
||||
do_gfx_action(
|
||||
command.force,
|
||||
Command::new("systemctl")
|
||||
.arg("restart")
|
||||
.arg("display-manager.service"),
|
||||
"Restart display-manager server",
|
||||
"Please restart display-manager when ready",
|
||||
)?;
|
||||
std::process::exit(1)
|
||||
}
|
||||
_ => std::process::exit(-1),
|
||||
}
|
||||
std::process::exit(-1)
|
||||
}
|
||||
if command.get {
|
||||
let res = writer.get_gfx_mode()?;
|
||||
println!("Current graphics mode: {}", res);
|
||||
}
|
||||
if command.pow {
|
||||
let res = writer.get_gfx_pwr()?;
|
||||
if res.contains("active") {
|
||||
println!("Current power status: {}", Red.paint(&format!("{}", res)));
|
||||
} else {
|
||||
println!("Current power status: {}", Green.paint(&format!("{}", res)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_gfx_action(
|
||||
no_confirm: bool,
|
||||
command: &mut Command,
|
||||
ask_msg: &str,
|
||||
cancel_msg: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("{}? y/n", ask_msg);
|
||||
|
||||
let mut buf = String::new();
|
||||
if no_confirm {
|
||||
let status = command.status()?;
|
||||
|
||||
if !status.success() {
|
||||
println!("systemctl: returned with {}", status);
|
||||
}
|
||||
}
|
||||
|
||||
std::io::stdin().read_line(&mut buf).expect("Input failed");
|
||||
let input = buf.chars().next().unwrap() as char;
|
||||
|
||||
if input == 'Y' || input == 'y' || no_confirm {
|
||||
let status = command.status()?;
|
||||
|
||||
if !status.success() {
|
||||
println!("systemctl: returned with {}", status);
|
||||
}
|
||||
} else {
|
||||
println!("{}", Red.paint(&format!("{}", cancel_msg)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "asus-nb"
|
||||
version = "0.15.0"
|
||||
version = "2.1.2"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -16,6 +16,10 @@ serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
yansi-term = "^0.1"
|
||||
rog_fan_curve = { version = "0.1", features = ["serde"] }
|
||||
zbus = "^1.8.0"
|
||||
zvariant = "^2.4.0"
|
||||
ctrl-gfx = { path = "../ctrl-gfx" }
|
||||
|
||||
[dev-dependencies]
|
||||
tinybmp = "^0.2.3"
|
||||
@@ -14,9 +14,10 @@ fn main() {
|
||||
let mut matrix = AniMeMatrix::new();
|
||||
|
||||
// Aligned left
|
||||
for px in pixels {
|
||||
for (i, px) in pixels.iter().enumerate() {
|
||||
if (px.x as usize / 2) < WIDTH && (px.y as usize) < HEIGHT && px.x % 2 == 0 {
|
||||
matrix.get_mut()[px.y as usize][px.x as usize / 2] = px.color as u8;
|
||||
let mut c = px.color as u32;
|
||||
matrix.get_mut()[px.y as usize][px.x as usize / 2] = c as u8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
writer.write_colour_block(&colours)?;
|
||||
|
||||
// can change 100 times per second, so need to slow it down
|
||||
//std::thread::sleep(std::time::Duration::from_millis(30));
|
||||
std::thread::sleep(std::time::Duration::from_millis(30));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
use crate::anime_matrix::AniMePacketType;
|
||||
use crate::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||
use dbus::channel::Sender;
|
||||
use dbus::{blocking::Connection, Message};
|
||||
use std::error::Error;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
const DBUS_ANIME_PATH: &str = "/org/asuslinux/Anime";
|
||||
pub const ANIME_PANE1_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
|
||||
pub const ANIME_PANE2_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
|
||||
|
||||
use crate::anime_matrix::{AniMeMatrix, AniMePacketType};
|
||||
use crate::DBUS_NAME;
|
||||
use dbus::blocking::{Connection, Proxy};
|
||||
use std::error::Error;
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use crate::dbus_anime::OrgAsuslinuxDaemon as OrgAsuslinuxDaemonAniMe;
|
||||
|
||||
/// Interface for the AniMe dot-matrix display
|
||||
///
|
||||
/// The resolution is 34x56 (1904) but only 1,215 LEDs in the top-left are used.
|
||||
/// The display is available only on select GA401 models.
|
||||
///
|
||||
/// Actual image ration when displayed is stretched width.
|
||||
/// Actual image ratio when displayed is stretched width.
|
||||
///
|
||||
/// Data structure should be nested array of [[u8; 33]; 56]
|
||||
pub struct AniMeDbusWriter {
|
||||
connection: Box<Connection>,
|
||||
block_time: u64,
|
||||
stop: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AniMeDbusWriter {
|
||||
@@ -33,46 +30,78 @@ impl AniMeDbusWriter {
|
||||
Ok(AniMeDbusWriter {
|
||||
connection: Box::new(connection),
|
||||
block_time: 25,
|
||||
stop: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
// Create D-Bus proxy
|
||||
fn new_proxy(&self) -> Proxy<&Connection> {
|
||||
self.connection
|
||||
.with_proxy(DBUS_NAME, DBUS_ANIME_PATH, Duration::from_millis(200))
|
||||
}
|
||||
|
||||
fn thread_sleep(&self) {
|
||||
thread::sleep(Duration::from_millis(self.block_time));
|
||||
}
|
||||
|
||||
pub fn write_image_to_buf(_buf: &mut AniMePacketType, _image_data: &[u8]) {
|
||||
unimplemented!("Image format is in progress of being worked out")
|
||||
}
|
||||
|
||||
/// Write an Animatrix image
|
||||
///
|
||||
/// The expected input here is *two* Vectors, 640 bytes in length. The two vectors
|
||||
/// are each one half of the full image write.
|
||||
/// The expected input here is *two* Vectors, 640 bytes in length.
|
||||
/// The two vectors are each one half of the full image write.
|
||||
///
|
||||
/// After each write a flush is written, it is assumed that this tells the device to
|
||||
/// go ahead and display the written bytes
|
||||
/// After each write a flush is written, it is assumed that this tells the
|
||||
/// device to go ahead and display the written bytes
|
||||
///
|
||||
/// # Note:
|
||||
/// The vectors are expected to contain the full sequence of bytes as follows
|
||||
/// # Note: The vectors are expected to contain the full sequence of bytes
|
||||
/// as follows
|
||||
///
|
||||
/// - Write packet 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
|
||||
/// - Write packet 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
|
||||
///
|
||||
/// Where led brightness is 0..255, low to high
|
||||
#[inline]
|
||||
pub fn write_image(&mut self, image: &mut AniMePacketType) -> Result<(), Box<dyn Error>> {
|
||||
if image[0][0] != ANIME_PANE1_PREFIX[0] && image[0][6] != ANIME_PANE1_PREFIX[6] {
|
||||
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
|
||||
}
|
||||
if image[1][0] != ANIME_PANE2_PREFIX[0] && image[1][6] != ANIME_PANE2_PREFIX[6] {
|
||||
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
|
||||
}
|
||||
pub fn write_image(&self, image: &mut AniMePacketType) -> Result<(), Box<dyn Error>> {
|
||||
let proxy = self.new_proxy();
|
||||
|
||||
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
|
||||
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
|
||||
|
||||
proxy.set_anime(vec![image[0].to_vec(), image[1].to_vec()])?;
|
||||
self.thread_sleep();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_leds_brightness(&self, led_brightness: u8) -> Result<(), Box<dyn Error>> {
|
||||
let mut anime_matrix = AniMeMatrix::new();
|
||||
|
||||
anime_matrix.fill_with(led_brightness);
|
||||
self.write_image(&mut AniMePacketType::from(anime_matrix))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn turn_on_off(&self, status: bool) -> Result<(), Box<dyn Error>> {
|
||||
let proxy = self.new_proxy();
|
||||
|
||||
proxy.set_on_off(status)?;
|
||||
self.thread_sleep();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn turn_boot_on_off(&self, status: bool) -> Result<(), Box<dyn Error>> {
|
||||
let proxy = self.new_proxy();
|
||||
|
||||
proxy.set_boot_on_off(status)?;
|
||||
self.thread_sleep();
|
||||
|
||||
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "AnimatrixWrite")?
|
||||
.append2(image[0].to_vec(), image[1].to_vec());
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
thread::sleep(Duration::from_millis(self.block_time));
|
||||
if self.stop.load(Ordering::Relaxed) {
|
||||
panic!("Got signal to stop!");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ impl AniMeMatrix {
|
||||
// Write the top block of LEDs (first 7 rows)
|
||||
if count < 6 {
|
||||
if count % 2 != 0 {
|
||||
print!(" ");
|
||||
print!(" ");
|
||||
} else {
|
||||
print!(" ");
|
||||
print!("");
|
||||
}
|
||||
let tmp = if count == 0 || count == 1 || count == 3 || count == 5 {
|
||||
row[1..].iter()
|
||||
@@ -56,7 +56,7 @@ impl AniMeMatrix {
|
||||
row.iter()
|
||||
};
|
||||
for x in tmp {
|
||||
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
|
||||
print!(" {}", RGB(*x, *x, *x).paint("XY"));
|
||||
}
|
||||
|
||||
println!();
|
||||
@@ -76,13 +76,13 @@ impl AniMeMatrix {
|
||||
let index = row.len() - prog_row_len;
|
||||
|
||||
if count % 2 == 0 {
|
||||
print!(" ");
|
||||
print!(" ");
|
||||
}
|
||||
for (i, x) in row.iter().enumerate() {
|
||||
if i >= index {
|
||||
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
|
||||
print!(" {}", RGB(*x, *x, *x).paint("XY"));
|
||||
} else {
|
||||
print!(" ");
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
@@ -17,7 +17,7 @@ pub const FLASH: u8 = 0x0c;
|
||||
pub const MULTISTATIC: u8 = 0x0d;
|
||||
pub const PER_KEY: u8 = 0xff;
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Colour(pub u8, pub u8, pub u8);
|
||||
impl From<cli_options::Colour> for Colour {
|
||||
fn from(c: cli_options::Colour) -> Self {
|
||||
@@ -30,7 +30,7 @@ impl Default for Colour {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum Speed {
|
||||
Low = 0xe1,
|
||||
Med = 0xeb,
|
||||
@@ -54,7 +54,7 @@ impl Default for Speed {
|
||||
/// Used for Rainbow mode.
|
||||
///
|
||||
/// Enum corresponds to the required integer value
|
||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum Direction {
|
||||
Right,
|
||||
Left,
|
||||
@@ -77,7 +77,7 @@ impl Default for Direction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct TwoColourSpeed {
|
||||
pub colour: Colour,
|
||||
pub colour2: Colour,
|
||||
@@ -93,7 +93,7 @@ impl From<cli_options::TwoColourSpeed> for TwoColourSpeed {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct SingleSpeed {
|
||||
pub speed: Speed,
|
||||
}
|
||||
@@ -105,7 +105,7 @@ impl From<cli_options::SingleSpeed> for SingleSpeed {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct SingleColour {
|
||||
pub colour: Colour,
|
||||
}
|
||||
@@ -117,7 +117,7 @@ impl From<cli_options::SingleColour> for SingleColour {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct MultiColour {
|
||||
pub colour1: Colour,
|
||||
pub colour2: Colour,
|
||||
@@ -135,7 +135,7 @@ impl From<cli_options::MultiColour> for MultiColour {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct SingleSpeedDirection {
|
||||
pub direction: Direction,
|
||||
pub speed: Speed,
|
||||
@@ -149,7 +149,7 @@ impl From<cli_options::SingleSpeedDirection> for SingleSpeedDirection {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct SingleColourSpeed {
|
||||
pub colour: Colour,
|
||||
pub speed: Speed,
|
||||
@@ -163,7 +163,7 @@ impl From<cli_options::SingleColourSpeed> for SingleColourSpeed {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum AuraModes {
|
||||
Static(SingleColour),
|
||||
Breathe(TwoColourSpeed),
|
||||
|
||||
@@ -5,10 +5,14 @@ use std::str::FromStr;
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct LedBrightness {
|
||||
level: u8,
|
||||
level: Option<u8>,
|
||||
}
|
||||
impl LedBrightness {
|
||||
pub fn level(&self) -> u8 {
|
||||
pub fn new(level: Option<u8>) -> Self {
|
||||
LedBrightness { level }
|
||||
}
|
||||
|
||||
pub fn level(&self) -> Option<u8> {
|
||||
self.level
|
||||
}
|
||||
}
|
||||
@@ -18,17 +22,31 @@ impl FromStr for LedBrightness {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_lowercase();
|
||||
match s.as_str() {
|
||||
"off" => Ok(LedBrightness { level: 0x00 }),
|
||||
"low" => Ok(LedBrightness { level: 0x01 }),
|
||||
"med" => Ok(LedBrightness { level: 0x02 }),
|
||||
"high" => Ok(LedBrightness { level: 0x03 }),
|
||||
"off" => Ok(LedBrightness { level: Some(0x00) }),
|
||||
"low" => Ok(LedBrightness { level: Some(0x01) }),
|
||||
"med" => Ok(LedBrightness { level: Some(0x02) }),
|
||||
"high" => Ok(LedBrightness { level: Some(0x03) }),
|
||||
_ => {
|
||||
println!("Missing required argument, must be one of:\noff,low,med,high\n");
|
||||
print!(
|
||||
"{}\n{}\n",
|
||||
"Invalid argument, must be one of:", "off, low, med, high"
|
||||
);
|
||||
Err(AuraError::ParseBrightness)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToString for LedBrightness {
|
||||
fn to_string(&self) -> String {
|
||||
let s = match self.level {
|
||||
Some(0x00) => "low",
|
||||
Some(0x01) => "med",
|
||||
Some(0x02) => "high",
|
||||
_ => "unknown",
|
||||
};
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Colour(pub u8, pub u8, pub u8);
|
||||
@@ -213,3 +231,57 @@ impl Default for SetAuraBuiltin {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AniMeStatusValue {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
impl FromStr for AniMeStatusValue {
|
||||
type Err = AuraError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_lowercase();
|
||||
match s.as_str() {
|
||||
"on" => Ok(AniMeStatusValue::On),
|
||||
"off" => Ok(AniMeStatusValue::Off),
|
||||
_ => {
|
||||
print!("{}\n{}\n", "Invalid argument, must be one of:", "on, off");
|
||||
Err(AuraError::ParseAnime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<AniMeStatusValue> for bool {
|
||||
fn from(value: AniMeStatusValue) -> Self {
|
||||
match value {
|
||||
AniMeStatusValue::On => true,
|
||||
AniMeStatusValue::Off => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct AniMeLeds {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(
|
||||
no_long,
|
||||
required,
|
||||
short = "b",
|
||||
meta = "BYTE",
|
||||
help = "set all leds brightness value"
|
||||
)]
|
||||
led_brightness: u8,
|
||||
}
|
||||
impl AniMeLeds {
|
||||
pub fn led_brightness(&self) -> u8 {
|
||||
self.led_brightness
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
pub enum AniMeActions {
|
||||
#[options(help = "change all leds brightness")]
|
||||
Leds(AniMeLeds),
|
||||
}
|
||||
|
||||
@@ -1,55 +1,196 @@
|
||||
use super::*;
|
||||
use crate::cli_options::LedBrightness;
|
||||
use crate::fancy::KeyColourArray;
|
||||
use dbus::channel::Sender;
|
||||
use dbus::{blocking::Connection, channel::Token, Message};
|
||||
use crate::profile::ProfileEvent;
|
||||
use ctrl_gfx::vendors::GfxVendors;
|
||||
use dbus::{blocking::Connection, Message};
|
||||
use std::error::Error;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
Arc, Mutex,
|
||||
};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use crate::dbus_charge::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonCharge, OrgAsuslinuxDaemonNotifyCharge,
|
||||
};
|
||||
use crate::dbus_gfx::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonGfx, OrgAsuslinuxDaemonNotifyAction,
|
||||
OrgAsuslinuxDaemonNotifyGfx,
|
||||
};
|
||||
use crate::dbus_ledmode::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonLed, OrgAsuslinuxDaemonNotifyLed,
|
||||
};
|
||||
use crate::dbus_profile::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonProfile, OrgAsuslinuxDaemonNotifyProfile,
|
||||
};
|
||||
|
||||
// Signals separated out
|
||||
pub struct CtrlSignals {
|
||||
pub gfx_vendor_signal: Arc<Mutex<Option<String>>>,
|
||||
pub gfx_action_signal: Arc<Mutex<Option<String>>>,
|
||||
pub profile_signal: Arc<Mutex<Option<String>>>,
|
||||
pub ledmode_signal: Arc<Mutex<Option<AuraModes>>>,
|
||||
pub charge_signal: Arc<Mutex<Option<u8>>>,
|
||||
}
|
||||
|
||||
impl CtrlSignals {
|
||||
#[inline]
|
||||
pub fn new(connection: &Connection) -> Result<Self, Box<dyn Error>> {
|
||||
let proxy = connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
|
||||
let gfx_vendor_signal = Arc::new(Mutex::new(None));
|
||||
let gfx_res1 = gfx_vendor_signal.clone();
|
||||
|
||||
let _x = proxy.match_signal(
|
||||
move |sig: OrgAsuslinuxDaemonNotifyGfx, _: &Connection, _: &Message| {
|
||||
if let Ok(mut lock) = gfx_res1.lock() {
|
||||
*lock = Some(sig.vendor);
|
||||
}
|
||||
true
|
||||
},
|
||||
)?;
|
||||
|
||||
let gfx_action_signal = Arc::new(Mutex::new(None));
|
||||
let gfx_res1 = gfx_action_signal.clone();
|
||||
|
||||
let _x = proxy.match_signal(
|
||||
move |sig: OrgAsuslinuxDaemonNotifyAction, _: &Connection, _: &Message| {
|
||||
if let Ok(mut lock) = gfx_res1.lock() {
|
||||
*lock = Some(sig.action);
|
||||
}
|
||||
true
|
||||
},
|
||||
)?;
|
||||
|
||||
//
|
||||
let proxy = connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
|
||||
let profile_signal = Arc::new(Mutex::new(None));
|
||||
let prof_res1 = profile_signal.clone();
|
||||
|
||||
let _x = proxy.match_signal(
|
||||
move |sig: OrgAsuslinuxDaemonNotifyProfile, _: &Connection, _: &Message| {
|
||||
if let Ok(mut lock) = prof_res1.lock() {
|
||||
*lock = Some(sig.profile);
|
||||
}
|
||||
true
|
||||
},
|
||||
)?;
|
||||
|
||||
//
|
||||
let proxy = connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
|
||||
let ledmode_signal = Arc::new(Mutex::new(None));
|
||||
let led_res1 = ledmode_signal.clone();
|
||||
|
||||
let _x = proxy.match_signal(
|
||||
move |sig: OrgAsuslinuxDaemonNotifyLed, _: &Connection, _: &Message| {
|
||||
if let Ok(mut lock) = led_res1.lock() {
|
||||
if let Ok(dat) = serde_json::from_str(&sig.data) {
|
||||
*lock = Some(dat);
|
||||
}
|
||||
}
|
||||
true
|
||||
},
|
||||
)?;
|
||||
|
||||
//
|
||||
let proxy = connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Charge",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
|
||||
let charge_signal = Arc::new(Mutex::new(None));
|
||||
let charge_res1 = charge_signal.clone();
|
||||
|
||||
let _x = proxy.match_signal(
|
||||
move |sig: OrgAsuslinuxDaemonNotifyCharge, _: &Connection, _: &Message| {
|
||||
if let Ok(mut lock) = charge_res1.lock() {
|
||||
*lock = Some(sig.limit);
|
||||
}
|
||||
true
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(CtrlSignals {
|
||||
gfx_vendor_signal,
|
||||
gfx_action_signal,
|
||||
profile_signal,
|
||||
ledmode_signal,
|
||||
charge_signal,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified way to write a effect block
|
||||
pub struct AuraDbusClient {
|
||||
connection: Box<Connection>,
|
||||
block_time: u64,
|
||||
stop: Arc<AtomicBool>,
|
||||
stop_token: Token,
|
||||
signals: CtrlSignals,
|
||||
}
|
||||
|
||||
impl AuraDbusClient {
|
||||
#[inline]
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let connection = Connection::new_system()?;
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let stopper2 = stop.clone();
|
||||
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "KeyBacklightChanged");
|
||||
let stop_token = connection.add_match(match_rule, move |_: (), _, msg| {
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "NotifyLed");
|
||||
let stop1 = stop.clone();
|
||||
connection.add_match(match_rule, move |_: (), _, msg| {
|
||||
if msg.read1::<&str>().is_ok() {
|
||||
stopper2.store(true, Ordering::Relaxed);
|
||||
stop1.clone().store(true, Ordering::Relaxed);
|
||||
}
|
||||
true
|
||||
})?;
|
||||
|
||||
let signals = CtrlSignals::new(&connection)?;
|
||||
|
||||
Ok(AuraDbusClient {
|
||||
connection: Box::new(connection),
|
||||
block_time: 33333,
|
||||
stop,
|
||||
stop_token,
|
||||
signals,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn wait_gfx_changed(&self) -> Result<String, Box<dyn Error>> {
|
||||
loop {
|
||||
self.connection.process(Duration::from_millis(1))?;
|
||||
if let Ok(lock) = self.signals.gfx_action_signal.lock() {
|
||||
if let Some(stuff) = lock.as_ref() {
|
||||
return Ok(stuff.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This method must always be called before the very first write to initialise
|
||||
/// the keyboard LED EC in the correct mode
|
||||
#[inline]
|
||||
pub fn init_effect(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mode = AuraModes::PerKey(vec![vec![]]);
|
||||
let mut msg =
|
||||
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
|
||||
.append1(serde_json::to_string(&mode)?);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.set_led_mode(&serde_json::to_string(&mode)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -68,15 +209,13 @@ impl AuraDbusClient {
|
||||
vecs.push(v.to_vec());
|
||||
}
|
||||
let mode = AuraModes::PerKey(vecs);
|
||||
let mut msg =
|
||||
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
|
||||
.append1(serde_json::to_string(&mode)?);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
|
||||
self.write_keyboard_leds(&mode)?;
|
||||
|
||||
thread::sleep(Duration::from_micros(self.block_time));
|
||||
self.connection.process(Duration::from_micros(500))?;
|
||||
|
||||
if self.stop.load(Ordering::Relaxed) {
|
||||
self.connection.remove_match(self.stop_token)?;
|
||||
println!("Keyboard backlight was changed, exiting");
|
||||
std::process::exit(1)
|
||||
}
|
||||
@@ -85,29 +224,114 @@ impl AuraDbusClient {
|
||||
|
||||
#[inline]
|
||||
pub fn write_keyboard_leds(&self, mode: &AuraModes) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut msg =
|
||||
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
|
||||
.append1(serde_json::to_string(mode)?);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.set_led_mode(&serde_json::to_string(mode)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next_keyboard_led_mode(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.next_led_mode()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn prev_keyboard_led_mode(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.prev_led_mode()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_gfx_pwr(&self) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
let x = proxy.power()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_gfx_mode(&self) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
let x = proxy.vendor()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_gfx_mode(&self, vendor: GfxVendors) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_secs(30),
|
||||
);
|
||||
proxy.set_vendor(<&str>::from(&vendor))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next_fan_profile(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.next_profile()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_fan_mode(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetFanMode")?
|
||||
.append1(level);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.set_profile(&serde_json::to_string(&ProfileEvent::ChangeMode(level))?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_profile_command(
|
||||
&self,
|
||||
cmd: &ProfileEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.set_profile(&serde_json::to_string(cmd)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_charge_limit(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetChargeLimit")?
|
||||
.append1(level);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Charge",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
proxy.set_limit(level)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -117,8 +341,21 @@ impl AuraDbusClient {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
|
||||
pub fn get_led_brightness(&self) -> Result<LedBrightness, Box<dyn Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Led",
|
||||
Duration::from_secs(2),
|
||||
);
|
||||
match proxy.led_brightness()? {
|
||||
-1 => Ok(LedBrightness::new(None)),
|
||||
level => Ok(LedBrightness::new(Some(level as u8))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_brightness(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.write_keyboard_leds(&AuraModes::LedBrightness(level))?;
|
||||
Ok(String::new())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
27
asus-nb/src/dbus_anime.rs
Normal file
27
asus-nb/src/dbus_anime.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Anime -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn set_anime(&self, input: Vec<Vec<u8>>) -> Result<(), dbus::Error>;
|
||||
fn set_on_off(&self, status: bool) -> Result<(), dbus::Error>;
|
||||
fn set_boot_on_off(&self, status: bool) -> Result<(), dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn set_anime(&self, input: Vec<Vec<u8>>) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetAnime", (input,))
|
||||
}
|
||||
|
||||
fn set_on_off(&self, status: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetOnOff", (status,))
|
||||
}
|
||||
|
||||
fn set_boot_on_off(&self, status: bool) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetBootOnOff", (status,))
|
||||
}
|
||||
}
|
||||
45
asus-nb/src/dbus_charge.rs
Normal file
45
asus-nb/src/dbus_charge.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Charge -m None`, see https://github.com/diwic/dbus-rs
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn set_limit(&self, limit: u8) -> Result<(), dbus::Error>;
|
||||
fn limit(&self) -> Result<i16, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn set_limit(&self, limit: u8) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetLimit", (limit,))
|
||||
}
|
||||
|
||||
fn limit(&self) -> Result<i16, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Limit", ())
|
||||
.and_then(|r: (i16,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyCharge {
|
||||
pub limit: u8,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyCharge {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.limit, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyCharge {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyCharge { limit: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyCharge {
|
||||
const NAME: &'static str = "NotifyCharge";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
73
asus-nb/src/dbus_gfx.rs
Normal file
73
asus-nb/src/dbus_gfx.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Gfx -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn vendor(&self) -> Result<String, dbus::Error>;
|
||||
fn power(&self) -> Result<String, dbus::Error>;
|
||||
fn set_vendor(&self, vendor: &str) -> Result<(), dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn vendor(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Vendor", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn power(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Power", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn set_vendor(&self, vendor: &str) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetVendor", (vendor,))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyGfx {
|
||||
pub vendor: String,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyGfx {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.vendor, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyGfx {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyGfx { vendor: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyGfx {
|
||||
const NAME: &'static str = "NotifyGfx";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyAction {
|
||||
pub action: String,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyAction {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.action, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyAction {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyAction { action: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyAction {
|
||||
const NAME: &'static str = "NotifyAction";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
67
asus-nb/src/dbus_ledmode.rs
Normal file
67
asus-nb/src/dbus_ledmode.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Led -m None`, see https://github.com/diwic/dbus-rs
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn set_led_mode(&self, data: &str) -> Result<(), dbus::Error>;
|
||||
fn next_led_mode(&self) -> Result<(), dbus::Error>;
|
||||
fn prev_led_mode(&self) -> Result<(), dbus::Error>;
|
||||
fn led_mode(&self) -> Result<String, dbus::Error>;
|
||||
fn led_modes(&self) -> Result<String, dbus::Error>;
|
||||
fn led_brightness(&self) -> Result<i16, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn set_led_mode(&self, data: &str) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetLedMode", (data,))
|
||||
}
|
||||
|
||||
fn next_led_mode(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "NextLedMode", ())
|
||||
}
|
||||
|
||||
fn prev_led_mode(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "PrevLedMode", ())
|
||||
}
|
||||
|
||||
fn led_mode(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "LedMode", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn led_modes(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "LedModes", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn led_brightness(&self) -> Result<i16, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "LedBrightness", ())
|
||||
.and_then(|r: (i16,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyLed {
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyLed {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.data, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyLed {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyLed { data: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyLed {
|
||||
const NAME: &'static str = "NotifyLed";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
62
asus-nb/src/dbus_profile.rs
Normal file
62
asus-nb/src/dbus_profile.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Profile -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
|
||||
use dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn set_profile(&self, profile: &str) -> Result<(), dbus::Error>;
|
||||
fn next_profile(&self) -> Result<(), dbus::Error>;
|
||||
fn active_profile_name(&self) -> Result<String, dbus::Error>;
|
||||
fn profile(&self) -> Result<String, dbus::Error>;
|
||||
fn profiles(&self) -> Result<String, dbus::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
|
||||
for blocking::Proxy<'a, C>
|
||||
{
|
||||
fn set_profile(&self, profile: &str) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "SetProfile", (profile,))
|
||||
}
|
||||
|
||||
fn next_profile(&self) -> Result<(), dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "NextProfile", ())
|
||||
}
|
||||
|
||||
fn active_profile_name(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "ActiveProfileName", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn profile(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Profile", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn profiles(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Profiles", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyProfile {
|
||||
pub profile: String,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyProfile {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.profile, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyProfile {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyProfile { profile: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyProfile {
|
||||
const NAME: &'static str = "NotifyProfile";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuraError {
|
||||
ParseColour,
|
||||
ParseSpeed,
|
||||
ParseDirection,
|
||||
ParseBrightness,
|
||||
ParseAnime,
|
||||
}
|
||||
|
||||
impl fmt::Display for AuraError {
|
||||
@@ -15,6 +18,25 @@ impl fmt::Display for AuraError {
|
||||
AuraError::ParseSpeed => write!(f, "Could not parse speed"),
|
||||
AuraError::ParseDirection => write!(f, "Could not parse direction"),
|
||||
AuraError::ParseBrightness => write!(f, "Could not parse brightness"),
|
||||
AuraError::ParseAnime => write!(f, "Could not parse anime"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for AuraError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GraphicsError {
|
||||
ParseVendor,
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphicsError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GraphicsError::ParseVendor => write!(f, "Could not parse vendor name"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GraphicsError {}
|
||||
|
||||
@@ -6,6 +6,8 @@ pub const LED_MSG_LEN: usize = 17;
|
||||
pub mod aura_modes;
|
||||
use aura_modes::AuraModes;
|
||||
|
||||
pub mod profile;
|
||||
|
||||
/// Contains mostly only what is required for parsing CLI options
|
||||
pub mod cli_options;
|
||||
|
||||
@@ -21,6 +23,12 @@ pub mod anime_dbus;
|
||||
/// Helper functions for the AniMe display
|
||||
pub mod anime_matrix;
|
||||
|
||||
pub mod dbus_anime;
|
||||
pub mod dbus_charge;
|
||||
pub mod dbus_gfx;
|
||||
pub mod dbus_ledmode;
|
||||
pub mod dbus_profile;
|
||||
|
||||
pub mod error;
|
||||
|
||||
// static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
|
||||
|
||||
97
asus-nb/src/profile.rs
Normal file
97
asus-nb/src/profile.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use gumdrop::Options;
|
||||
use rog_fan_curve::{Curve, Fan};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ProfileEvent {
|
||||
Cli(ProfileCommand),
|
||||
ChangeMode(u8),
|
||||
Toggle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FanLevel {
|
||||
Normal,
|
||||
Boost,
|
||||
Silent,
|
||||
}
|
||||
|
||||
impl FromStr for FanLevel {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"normal" => Ok(FanLevel::Normal),
|
||||
"boost" => Ok(FanLevel::Boost),
|
||||
"silent" => Ok(FanLevel::Silent),
|
||||
_ => Err("Invalid fan level"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for FanLevel {
|
||||
fn from(n: u8) -> Self {
|
||||
match n {
|
||||
0 => FanLevel::Normal,
|
||||
1 => FanLevel::Boost,
|
||||
2 => FanLevel::Silent,
|
||||
_ => FanLevel::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FanLevel> for u8 {
|
||||
fn from(n: FanLevel) -> Self {
|
||||
match n {
|
||||
FanLevel::Normal => 0,
|
||||
FanLevel::Boost => 1,
|
||||
FanLevel::Silent => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FanLevel> for u8 {
|
||||
fn from(n: &FanLevel) -> Self {
|
||||
match n {
|
||||
FanLevel::Normal => 0,
|
||||
FanLevel::Boost => 1,
|
||||
FanLevel::Silent => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
|
||||
let curve = Curve::from_config_str(data)?;
|
||||
if let Err(err) = curve.check_safety(Fan::Cpu) {
|
||||
return Err(format!("Unsafe curve {:?}", err));
|
||||
}
|
||||
if let Err(err) = curve.check_safety(Fan::Gpu) {
|
||||
return Err(format!("Unsafe curve {:?}", err));
|
||||
}
|
||||
Ok(curve)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options, Serialize, Deserialize)]
|
||||
pub struct ProfileCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "toggle to next profile in list")]
|
||||
pub next: bool,
|
||||
#[options(help = "create the profile if it doesn't exist")]
|
||||
pub create: bool,
|
||||
|
||||
#[options(help = "enable or disable cpu turbo")]
|
||||
pub turbo: Option<bool>,
|
||||
#[options(help = "set min cpu scaling (intel)")]
|
||||
pub min_percentage: Option<u8>,
|
||||
#[options(help = "set max cpu scaling (intel)")]
|
||||
pub max_percentage: Option<u8>,
|
||||
|
||||
#[options(meta = "PWR", help = "<silent, normal, boost>")]
|
||||
pub preset: Option<FanLevel>,
|
||||
#[options(parse(try_from_str = "parse_fan_curve"), help = "set fan curve")]
|
||||
pub curve: Option<Curve>,
|
||||
#[options(free)]
|
||||
pub profile: Option<String>,
|
||||
}
|
||||
21
asus-notify/Cargo.toml
Normal file
21
asus-notify/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "asus-notify"
|
||||
version = "2.0.4"
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# serialisation
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
dbus = { version = "^0.8" }
|
||||
asus-nb = { path = "../asus-nb" }
|
||||
asus-nb-ctrl = { path = "../asus-nb-ctrl" }
|
||||
|
||||
[dependencies.notify-rust]
|
||||
version = "^4.0.0"
|
||||
default-features = false
|
||||
features = ["z"]
|
||||
107
asus-notify/src/main.rs
Normal file
107
asus-notify/src/main.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use asus_nb::core_dbus::CtrlSignals;
|
||||
use daemon::config::{Config, Profile};
|
||||
use dbus::blocking::Connection;
|
||||
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Version {}", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
let mut cfg = Config::read_new()?;
|
||||
let mut last_profile = String::new();
|
||||
|
||||
let connection = Connection::new_system()?;
|
||||
let signals = CtrlSignals::new(&connection)?;
|
||||
|
||||
let mut last_profile_notif: Option<NotificationHandle> = None;
|
||||
let mut last_led_notif: Option<NotificationHandle> = None;
|
||||
let mut last_gfx_notif: Option<NotificationHandle> = None;
|
||||
let mut last_chrg_notif: Option<NotificationHandle> = None;
|
||||
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
connection.process(std::time::Duration::from_millis(200))?;
|
||||
|
||||
if let Ok(mut lock) = signals.gfx_vendor_signal.lock() {
|
||||
if let Some(vendor) = lock.take() {
|
||||
if let Some(notif) = last_gfx_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!("Graphics mode changed to {}", vendor))?;
|
||||
last_gfx_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.charge_signal.lock() {
|
||||
if let Some(limit) = lock.take() {
|
||||
if let Some(notif) = last_chrg_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!("Battery charge limit changed to {}", limit))?;
|
||||
last_led_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.ledmode_signal.lock() {
|
||||
if let Some(ledmode) = lock.take() {
|
||||
if let Some(notif) = last_led_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!(
|
||||
"Keyboard LED mode changed to {}",
|
||||
<&str>::from(&ledmode)
|
||||
))?;
|
||||
last_led_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to do the config read because of a limitation preventing
|
||||
// easy dbus notification from the profile controller
|
||||
cfg.read();
|
||||
if last_profile != cfg.active_profile {
|
||||
if let Some(notif) = last_profile_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
||||
let x = do_thermal_notif(&profile, &cfg.active_profile)?;
|
||||
last_profile_notif = Some(x);
|
||||
last_profile = cfg.active_profile.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_thermal_notif(profile: &Profile, label: &str) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||
let fan = profile.fan_preset;
|
||||
let turbo = if profile.turbo { "enabled" } else { "disabled" };
|
||||
let icon = match fan {
|
||||
0 => "asus_notif_yellow",
|
||||
1 => "asus_notif_red",
|
||||
2 => "asus_notif_green",
|
||||
_ => "asus_notif_red",
|
||||
};
|
||||
let x = Notification::new()
|
||||
.summary("ASUS ROG")
|
||||
.body(&format!(
|
||||
"Thermal profile changed to {}, turbo {}",
|
||||
label.to_uppercase(),
|
||||
turbo
|
||||
))
|
||||
.hint(Hint::Resident(true))
|
||||
.timeout(2000)
|
||||
.hint(Hint::Category("device".into()))
|
||||
//.hint(Hint::Transient(true))
|
||||
.icon(icon)
|
||||
.show()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn do_notif(body: &str) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||
let x = Notification::new()
|
||||
.summary("ASUS ROG")
|
||||
.body(body)
|
||||
.timeout(2000)
|
||||
.show()?;
|
||||
Ok(x)
|
||||
}
|
||||
19
ctrl-gfx/Cargo.toml
Normal file
19
ctrl-gfx/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "ctrl-gfx"
|
||||
version = "2.1.4"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
description = "Fine control of laptop GPU"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
sysfs-class = "^0.1.2"
|
||||
log = "^0.4"
|
||||
|
||||
zbus = { version = "1.8.0", optional = true }
|
||||
zvariant = { version = "2.4.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["use-zbus"]
|
||||
use-zbus = ["zbus", "zvariant"]
|
||||
372
ctrl-gfx/src/ctrl_gfx.rs
Normal file
372
ctrl-gfx/src/ctrl_gfx.rs
Normal file
@@ -0,0 +1,372 @@
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::vendors::*;
|
||||
use crate::*;
|
||||
use crate::{error::GfxError, system::*};
|
||||
|
||||
pub struct CtrlGraphics {
|
||||
bus: PciBus,
|
||||
_amd: Vec<GraphicsDevice>,
|
||||
_intel: Vec<GraphicsDevice>,
|
||||
nvidia: Vec<GraphicsDevice>,
|
||||
#[allow(dead_code)]
|
||||
other: Vec<GraphicsDevice>,
|
||||
initfs_cmd: Option<Command>,
|
||||
}
|
||||
|
||||
trait Dbus {
|
||||
fn vendor(&self) -> String;
|
||||
fn power(&self) -> String;
|
||||
fn set_vendor(&mut self, vendor: String);
|
||||
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()>;
|
||||
fn notify_action(&self, action: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl Dbus for CtrlGraphics {
|
||||
fn vendor(&self) -> String {
|
||||
Self::get_vendor().unwrap_or_else(|err| format!("Get vendor failed: {}", err))
|
||||
}
|
||||
|
||||
fn power(&self) -> String {
|
||||
Self::get_runtime_status().unwrap_or_else(|err| format!("Get power status failed: {}", err))
|
||||
}
|
||||
|
||||
fn set_vendor(&mut self, vendor: String) {
|
||||
if let Ok(tmp) = GfxVendors::from_str(&vendor) {
|
||||
let action = self.set(tmp).unwrap_or_else(|err| {
|
||||
warn!("{}", err);
|
||||
format!("Failed: {}", err.to_string())
|
||||
});
|
||||
self.notify_gfx(&vendor)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_action(&action)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_action(&self, action: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl CtrlGraphics {
|
||||
pub fn new() -> std::io::Result<CtrlGraphics> {
|
||||
let bus = PciBus::new()?;
|
||||
|
||||
info!("Rescanning PCI bus");
|
||||
bus.rescan()?;
|
||||
|
||||
let devs = PciDevice::all()?;
|
||||
|
||||
let functions = |parent: &PciDevice| -> Vec<PciDevice> {
|
||||
let mut functions = Vec::new();
|
||||
if let Some(parent_slot) = parent.id().split('.').next() {
|
||||
for func in devs.iter() {
|
||||
if let Some(func_slot) = func.id().split('.').next() {
|
||||
if func_slot == parent_slot {
|
||||
info!("{}: Function for {}", func.id(), parent.id());
|
||||
functions.push(func.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
functions
|
||||
};
|
||||
|
||||
let mut amd = Vec::new();
|
||||
let mut intel = Vec::new();
|
||||
let mut nvidia = Vec::new();
|
||||
let mut other = Vec::new();
|
||||
for dev in devs.iter() {
|
||||
let c = dev.class()?;
|
||||
if 0x03 == (c >> 16) & 0xFF {
|
||||
match dev.vendor()? {
|
||||
0x1002 => {
|
||||
info!("{}: AMD graphics", dev.id());
|
||||
amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
0x10DE => {
|
||||
info!("{}: NVIDIA graphics", dev.id());
|
||||
nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
0x8086 => {
|
||||
info!("{}: Intel graphics", dev.id());
|
||||
intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
vendor => {
|
||||
info!("{}: Other({:X}) graphics", dev.id(), vendor);
|
||||
other.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut initfs_cmd = None;
|
||||
|
||||
if Path::new(INITRAMFS_PATH).exists() {
|
||||
let mut cmd = Command::new("update-initramfs");
|
||||
cmd.arg("-u");
|
||||
initfs_cmd = Some(cmd);
|
||||
info!("Using initramfs update command 'update-initramfs'");
|
||||
}
|
||||
// } else if Path::new(DRACUT_PATH).exists() {
|
||||
// let mut cmd = Command::new("dracut");
|
||||
// cmd.arg("-f");
|
||||
// initfs_cmd = Some(cmd);
|
||||
// info!("Using initramfs update command 'dracut'");
|
||||
// }
|
||||
|
||||
Ok(CtrlGraphics {
|
||||
bus,
|
||||
_amd: amd,
|
||||
_intel: intel,
|
||||
nvidia,
|
||||
other,
|
||||
initfs_cmd,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Gfx".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlGraphics: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn reload(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
self.auto_power()?;
|
||||
info!("Reloaded gfx mode: {:?}", CtrlGraphics::get_vendor()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn can_switch(&self) -> bool {
|
||||
// !self.nvidia.is_empty() && (!self.intel.is_empty() || !self.amd.is_empty())
|
||||
// }
|
||||
|
||||
fn get_prime_discrete() -> Result<String, GfxError> {
|
||||
let s = std::fs::read_to_string(PRIME_DISCRETE_PATH)
|
||||
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn set_prime_discrete(mode: &str) -> Result<(), GfxError> {
|
||||
std::fs::write(PRIME_DISCRETE_PATH, mode)
|
||||
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Associated method to get which vendor mode is set
|
||||
pub fn get_vendor() -> Result<String, GfxError> {
|
||||
let mode = match Self::get_prime_discrete() {
|
||||
Ok(m) => m,
|
||||
Err(_) => "nvidia".to_string(),
|
||||
};
|
||||
let modules = Module::all().map_err(|err| GfxError::Read("get_vendor".into(), err))?;
|
||||
|
||||
let driver_loaded = if modules
|
||||
.iter()
|
||||
.any(|module| module.name == "nouveau" || module.name == "nvidia")
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let vendor = if mode == "off" {
|
||||
if driver_loaded {
|
||||
info!("dGPU driver loaded for compute mode");
|
||||
"compute".to_string()
|
||||
} else {
|
||||
info!("No dGPU driver loaded");
|
||||
"integrated".to_string()
|
||||
}
|
||||
} else {
|
||||
info!("Assuming dGPU driver loaded");
|
||||
if mode == "on-demand" {
|
||||
"hybrid".to_string()
|
||||
} else {
|
||||
"nvidia".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(vendor)
|
||||
}
|
||||
|
||||
pub fn is_switching_prime_modes(vendor: &GfxVendors) -> Result<bool, GfxError> {
|
||||
let prev_mode = GfxVendors::from_str(&Self::get_vendor()?)?;
|
||||
let x = (prev_mode == GfxVendors::Hybrid || prev_mode == GfxVendors::Nvidia)
|
||||
&& (*vendor == GfxVendors::Hybrid || *vendor == GfxVendors::Nvidia);
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
/// Write out config files if required, enable/disable relevant services, and update the ramdisk
|
||||
pub fn set(&mut self, vendor: GfxVendors) -> Result<String, GfxError> {
|
||||
//self.switchable_or_fail()?;
|
||||
|
||||
let mode = if vendor == GfxVendors::Hybrid {
|
||||
"on-demand\n"
|
||||
} else if vendor == GfxVendors::Nvidia {
|
||||
"on\n"
|
||||
} else {
|
||||
// Integrated or Compute
|
||||
"off\n"
|
||||
};
|
||||
|
||||
info!("Setting {} to {}", PRIME_DISCRETE_PATH, mode);
|
||||
Self::set_prime_discrete(mode)?;
|
||||
|
||||
// Switching from hybrid to/from nvidia shouldn't require a ramdisk update
|
||||
// or a reboot.
|
||||
let no_reboot = Self::is_switching_prime_modes(&vendor)?;
|
||||
|
||||
{
|
||||
info!("Writing {}", MODPROBE_PATH);
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(MODPROBE_PATH)
|
||||
.map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
let text = if vendor == GfxVendors::Hybrid {
|
||||
MODPROBE_HYBRID
|
||||
} else if vendor == GfxVendors::Compute {
|
||||
MODPROBE_COMPUTE
|
||||
} else if vendor == GfxVendors::Nvidia {
|
||||
MODPROBE_NVIDIA
|
||||
} else {
|
||||
MODPROBE_INTEGRATED
|
||||
};
|
||||
|
||||
file.write_all(text)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||
}
|
||||
|
||||
info!("Writing {}", PRIMARY_GPU_XORG_PATH);
|
||||
|
||||
// begin section for non-separated Nvidia xorg modules
|
||||
// eg, not put in their own directory
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(PRIMARY_GPU_XORG_PATH)
|
||||
.map_err(|err| GfxError::Write(PRIMARY_GPU_XORG_PATH.into(), err))?;
|
||||
|
||||
let text = if vendor == GfxVendors::Nvidia {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
||||
} else {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
|
||||
};
|
||||
|
||||
file.write_all(&text)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
let action = if vendor == GfxVendors::Nvidia {
|
||||
info!("Enabling nvidia-fallback.service");
|
||||
"enable"
|
||||
} else {
|
||||
info!("Disabling nvidia-fallback.service");
|
||||
"disable"
|
||||
};
|
||||
|
||||
let status = Command::new("systemctl")
|
||||
.arg(action)
|
||||
.arg("nvidia-fallback.service")
|
||||
.status()
|
||||
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
|
||||
|
||||
if !status.success() {
|
||||
// Error is ignored in case this service is removed
|
||||
warn!(
|
||||
"systemctl: {} (ignore warning if service does not exist!)",
|
||||
status
|
||||
);
|
||||
}
|
||||
|
||||
let mut required_action = GfxCtrlAction::None;
|
||||
if !no_reboot {
|
||||
info!("Updating initramfs");
|
||||
if let Some(cmd) = self.initfs_cmd.as_mut() {
|
||||
let status = cmd
|
||||
.status()
|
||||
.map_err(|err| GfxError::Write(format!("{:?}", cmd), err))?;
|
||||
if !status.success() {
|
||||
error!("Ram disk update failed");
|
||||
} else {
|
||||
info!("Successfully updated iniramfs");
|
||||
}
|
||||
}
|
||||
required_action = GfxCtrlAction::Reboot;
|
||||
} else if no_reboot {
|
||||
required_action = GfxCtrlAction::RestartX;
|
||||
}
|
||||
|
||||
Ok(required_action.into())
|
||||
}
|
||||
|
||||
// pub fn get_power(&self) -> Option<bool> {
|
||||
// if self.can_switch() {
|
||||
// return Some(self.nvidia.iter().any(GraphicsDevice::exists));
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
pub fn get_runtime_status() -> Result<String, GfxError> {
|
||||
const PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
|
||||
let buf = std::fs::read_to_string(PATH).map_err(|err| GfxError::Read(PATH.into(), err))?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn set_power(&self, power: bool) -> Result<(), GfxError> {
|
||||
// self.switchable_or_fail()?;
|
||||
|
||||
if power {
|
||||
info!("Enabling graphics power");
|
||||
self.bus
|
||||
.rescan()
|
||||
.map_err(|err| GfxError::Bus("bus rescan error".into(), err))?;
|
||||
} else {
|
||||
info!("Disabling graphics power");
|
||||
|
||||
// Unbind NVIDIA graphics devices and their functions
|
||||
let unbinds = self.nvidia.iter().map(|dev| dev.unbind());
|
||||
|
||||
// Remove NVIDIA graphics devices and their functions
|
||||
let removes = self.nvidia.iter().map(|dev| dev.remove());
|
||||
|
||||
Result::from_iter(unbinds.chain(removes))
|
||||
.map_err(|err| GfxError::Command("device unbind error".into(), err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn auto_power(&self) -> Result<(), GfxError> {
|
||||
let vendor = CtrlGraphics::get_vendor()?;
|
||||
self.set_power(vendor != "integrated")
|
||||
}
|
||||
}
|
||||
30
ctrl-gfx/src/error.rs
Normal file
30
ctrl-gfx/src/error.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GfxError {
|
||||
ParseVendor,
|
||||
Path(String, std::io::Error),
|
||||
Read(String, std::io::Error),
|
||||
Write(String, std::io::Error),
|
||||
Module(String, std::io::Error),
|
||||
Bus(String, std::io::Error),
|
||||
Command(String, std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for GfxError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
|
||||
GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
|
||||
GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||
GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||
GfxError::Module(func, error) => write!(f, "Module error: {}: {}", func, error),
|
||||
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
|
||||
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for GfxError {}
|
||||
57
ctrl-gfx/src/lib.rs
Normal file
57
ctrl-gfx/src/lib.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
pub mod vendors;
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub mod ctrl_gfx;
|
||||
|
||||
pub mod system;
|
||||
|
||||
const PRIME_DISCRETE_PATH: &str = "/etc/prime-discrete";
|
||||
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
|
||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||
// const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||
|
||||
static MODPROBE_NVIDIA: &[u8] = MODPROBE_HYBRID;
|
||||
|
||||
static MODPROBE_HYBRID: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
alias i2c_nvidia_gpu off
|
||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
||||
options nvidia-drm modeset=1
|
||||
"#;
|
||||
|
||||
static MODPROBE_COMPUTE: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
alias i2c_nvidia_gpu off
|
||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
||||
options nvidia-drm modeset=0
|
||||
"#;
|
||||
|
||||
static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
blacklist nouveau
|
||||
blacklist nvidia
|
||||
blacklist nvidia-drm
|
||||
blacklist nvidia-modeset
|
||||
alias i2c_nvidia_gpu off
|
||||
alias nouveau off
|
||||
alias nvidia off
|
||||
alias nvidia-drm off
|
||||
alias nvidia-modeset off
|
||||
"#;
|
||||
|
||||
const PRIMARY_GPU_XORG_PATH: &str = "/etc/X11/xorg.conf.d/90-nvidia-primary.conf";
|
||||
|
||||
static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd
|
||||
Section "OutputClass"
|
||||
Identifier "nvidia"
|
||||
MatchDriver "nvidia-drm"
|
||||
Driver "nvidia"
|
||||
Option "AllowEmptyInitialConfiguration"
|
||||
Option "AllowExternalGpus""#;
|
||||
|
||||
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
|
||||
Option "PrimaryGPU" "true""#;
|
||||
|
||||
static PRIMARY_GPU_END: &[u8] = br#"
|
||||
EndSection"#;
|
||||
127
ctrl-gfx/src/system.rs
Normal file
127
ctrl-gfx/src/system.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use log::{error, info, warn};
|
||||
use std::fs::read_to_string;
|
||||
use std::{fs::write, io, path::PathBuf};
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
fn parse(line: &str) -> io::Result<Module> {
|
||||
let mut parts = line.split(' ');
|
||||
|
||||
let name = parts
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
|
||||
|
||||
Ok(Module {
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all() -> io::Result<Vec<Module>> {
|
||||
let mut modules = Vec::new();
|
||||
|
||||
let data = read_to_string("/proc/modules")?;
|
||||
for line in data.lines() {
|
||||
let module = Module::parse(line)?;
|
||||
modules.push(module);
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciBus {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl PciBus {
|
||||
pub fn new() -> io::Result<PciBus> {
|
||||
let path = PathBuf::from("/sys/bus/pci");
|
||||
if path.is_dir() {
|
||||
Ok(PciBus { path })
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"pci directory not found",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rescan(&self) -> io::Result<()> {
|
||||
write(self.path.join("rescan"), "1")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphicsDevice {
|
||||
id: String,
|
||||
functions: Vec<PciDevice>,
|
||||
}
|
||||
|
||||
impl GraphicsDevice {
|
||||
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
|
||||
GraphicsDevice { id, functions }
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> bool {
|
||||
self.functions.iter().any(|func| func.path().exists())
|
||||
}
|
||||
|
||||
pub fn unbind(&self) -> Result<(), std::io::Error> {
|
||||
for func in self.functions.iter() {
|
||||
if func.path().exists() {
|
||||
match func.driver() {
|
||||
Ok(driver) => {
|
||||
info!("{}: Unbinding {}", driver.id(), func.id());
|
||||
unsafe {
|
||||
driver.unbind(&func).map_err(|err| {
|
||||
error!("gfx unbind: {}", err);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
io::ErrorKind::NotFound => (),
|
||||
_ => {
|
||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
||||
return Err(err);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove(&self) -> Result<(), std::io::Error> {
|
||||
for func in self.functions.iter() {
|
||||
if func.path().exists() {
|
||||
match func.driver() {
|
||||
Ok(driver) => {
|
||||
error!("{}: in use by {}", func.id(), driver.id());
|
||||
}
|
||||
Err(why) => match why.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
info!("{}: Removing", func.id());
|
||||
unsafe {
|
||||
// ignore errors and carry on
|
||||
if let Err(err) = func.remove() {
|
||||
error!("gfx remove: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Remove device failed");
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
warn!("{}: Already removed", func.id());
|
||||
}
|
||||
}
|
||||
info!("Removed all gfx devices");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
85
ctrl-gfx/src/vendors.rs
Normal file
85
ctrl-gfx/src/vendors.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum GfxVendors {
|
||||
Nvidia,
|
||||
Integrated,
|
||||
Compute,
|
||||
Hybrid,
|
||||
}
|
||||
|
||||
use crate::error::GfxError;
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromStr for GfxVendors {
|
||||
type Err = GfxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"nvidia" => Ok(GfxVendors::Nvidia),
|
||||
"hybrid" => Ok(GfxVendors::Hybrid),
|
||||
"compute" => Ok(GfxVendors::Compute),
|
||||
"integrated" => Ok(GfxVendors::Integrated),
|
||||
"nvidia\n" => Ok(GfxVendors::Nvidia),
|
||||
"hybrid\n" => Ok(GfxVendors::Hybrid),
|
||||
"compute\n" => Ok(GfxVendors::Compute),
|
||||
"integrated\n" => Ok(GfxVendors::Integrated),
|
||||
_ => Err(GfxError::ParseVendor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxVendors> for &str {
|
||||
fn from(mode: &GfxVendors) -> Self {
|
||||
match mode {
|
||||
GfxVendors::Nvidia => "nvidia",
|
||||
GfxVendors::Hybrid => "hybrid",
|
||||
GfxVendors::Compute => "compute",
|
||||
GfxVendors::Integrated => "integrated",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GfxCtrlAction {
|
||||
Reboot,
|
||||
RestartX,
|
||||
None,
|
||||
}
|
||||
|
||||
impl FromStr for GfxCtrlAction {
|
||||
type Err = GfxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"reboot" => Ok(GfxCtrlAction::Reboot),
|
||||
"restartx" => Ok(GfxCtrlAction::RestartX),
|
||||
"none" => Ok(GfxCtrlAction::None),
|
||||
_ => Err(GfxError::ParseVendor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxCtrlAction> for &str {
|
||||
fn from(mode: &GfxCtrlAction) -> Self {
|
||||
match mode {
|
||||
GfxCtrlAction::Reboot => "reboot",
|
||||
GfxCtrlAction::RestartX => "restartx",
|
||||
GfxCtrlAction::None => "none",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxCtrlAction> for String {
|
||||
fn from(mode: &GfxCtrlAction) -> Self {
|
||||
match mode {
|
||||
GfxCtrlAction::Reboot => "reboot".into(),
|
||||
GfxCtrlAction::RestartX => "restartx".into(),
|
||||
GfxCtrlAction::None => "none".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GfxCtrlAction> for String {
|
||||
fn from(mode: GfxCtrlAction) -> Self {
|
||||
(&mode).into()
|
||||
}
|
||||
}
|
||||
7
data/90-asusd-nvidia-pm.rules
Normal file
7
data/90-asusd-nvidia-pm.rules
Normal file
@@ -0,0 +1,7 @@
|
||||
# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
|
||||
|
||||
# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
|
||||
4
data/90-nvidia-screen-G05.conf
Normal file
4
data/90-nvidia-screen-G05.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
Section "ServerLayout"
|
||||
Identifier "layout"
|
||||
Option "AllowNVIDIAGPUScreens"
|
||||
EndSection
|
||||
50
data/_asusctl
Normal file
50
data/_asusctl
Normal file
@@ -0,0 +1,50 @@
|
||||
function _asusctl() {
|
||||
local line
|
||||
|
||||
_arguments -C \
|
||||
{-h,--help}'[print help message]' \
|
||||
{-v,--version}'[print version number]' \
|
||||
{-k,--kbd-bright}':[Set keyboard brightness (off, low, med, high)]' \
|
||||
{-p,--pwr-profile}':[Set power profile (silent, normal, boost)]' \
|
||||
{-c,--chg-limit}':[Set charging limit (20-100)]' \
|
||||
': :((led-mode\:"Set the keyboard lighting from built-in modes" profile\:"Create and configure profiles" graphics\:"Set the graphics mode"))' \
|
||||
'*::arg:->args'
|
||||
case $line[1] in
|
||||
led-mode)
|
||||
_arguments ': :((static\:"set a single static colour"
|
||||
breathe\:"pulse between one or two colours"
|
||||
strobe\:"strobe through all colours"
|
||||
rainbow\:"rainbow cycling in one of four directions"
|
||||
star\:"rain pattern mimicking raindrops"
|
||||
rain\:"rain pattern of three preset colours"
|
||||
highlight\:"pressed keys are highlighted to fade"
|
||||
laser\:"pressed keys generate horizontal laser"
|
||||
ripple\:"pressed keys ripple outwards like a splash"
|
||||
pulse\:"set a rapid pulse"
|
||||
comet\:"set a vertical line zooming from left"
|
||||
flash\:"set a wide vertical line zooming from left"
|
||||
multi-static\:"4-zone multi-colour"))' \
|
||||
{-h,--help}'[print help message]' \
|
||||
'-c:[set the RGB value e.g, ff00ff]' \
|
||||
'-s:[set the speed (low, med, high)]'
|
||||
;;
|
||||
profile)
|
||||
_arguments {-h,--help}'[print help message]' \
|
||||
{-c,--create}"[create the profile if it doesn't exist]" \
|
||||
{-t,--turbo}':[enable or disable cpu turbo]' \
|
||||
{-m,--min-percentage}':[set min cpu scaling (intel)]' \
|
||||
{-M,--max-percentage}':[set max cpu scaling (intel)]' \
|
||||
{-p,--preset}':[<silent, normal, boost>]' \
|
||||
{-C,--curve}':[set fan curve]'
|
||||
|
||||
;;
|
||||
graphics)
|
||||
_arguments {-h,--help}'[print help message]' \
|
||||
{-m,--mode}':[Set graphics mode (nvidia, hybrid, compute, integrated)]' \
|
||||
{-g,--get}'[Get the current mode]' \
|
||||
{-p,--pow}'[Get the current power status]' \
|
||||
{-f,--force}'[Do not ask for confirmation]'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
compdef _asusctl asusctl
|
||||
14
data/asus-notify.service
Normal file
14
data/asus-notify.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=ASUS Notifications
|
||||
StartLimitInterval=200
|
||||
StartLimitBurst=2
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/sleep 2
|
||||
ExecStart=/usr/bin/asus-notify
|
||||
Restart=on-failure
|
||||
RestartSec=1
|
||||
Type=simple
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
12
data/asusd-alt.service
Normal file
12
data/asusd-alt.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=ASUS Notebook Control
|
||||
After=basic.target syslog.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/asusd
|
||||
Restart=on-failure
|
||||
Type=dbus
|
||||
BusName=org.asuslinux.Daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -4,9 +4,9 @@ board_names = ["GX502", "GX701", "G531", "GL531", "G532"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["GX531", "G512LV", "G712LV"]
|
||||
led_modes = [0, 1, 2, 3, 10, 13]
|
||||
prod_family = "Zephyrus M"
|
||||
board_names = ["GU502GV"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Zephyrus"
|
||||
@@ -15,14 +15,19 @@ led_modes = [0, 1, 2, 3, 10, 13]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G512LI", "G712LI"]
|
||||
led_modes = [0, 1, 2, 3, 10]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Strix Scar 3"
|
||||
board_names = ["G531GW"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["GX531", "G512LV", "G712LV"]
|
||||
led_modes = [0, 1, 2, 3, 10, 13]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G512LI", "G712LI"]
|
||||
led_modes = [0, 1, 2, 3, 10]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Strix"
|
||||
board_names = ["G731GV", "G731GW", "G531GV"]
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
[Unit]
|
||||
Description=ASUS Notebook Control
|
||||
StartLimitInterval=200
|
||||
StartLimitBurst=2
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/asusd
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
Type=dbus
|
||||
BusName=org.asuslinux.Daemon
|
||||
BIN
data/icons/asus_notif_blue.png
Normal file
BIN
data/icons/asus_notif_blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
BIN
data/icons/asus_notif_green.png
Normal file
BIN
data/icons/asus_notif_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
BIN
data/icons/asus_notif_orange.png
Normal file
BIN
data/icons/asus_notif_orange.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
BIN
data/icons/asus_notif_red.png
Normal file
BIN
data/icons/asus_notif_red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
BIN
data/icons/asus_notif_white.png
Normal file
BIN
data/icons/asus_notif_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
BIN
data/icons/asus_notif_yellow.png
Normal file
BIN
data/icons/asus_notif_yellow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
22
debian/asus-nb-ctrl.postinst
vendored
22
debian/asus-nb-ctrl.postinst
vendored
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
CONF='/etc/asusd.conf'
|
||||
if [ -f $CONF ]; then
|
||||
mv $CONF $CONF.save
|
||||
fi
|
||||
systemctl unmask asusd.service
|
||||
#ROG=$(lsusb |grep 0b05 |cut -d ' ' -f 6 |cut -d ':' -f 2)
|
||||
#sed -i -e "s|==/"1866/"|==/"${ROG}/"|g" /etc/udev/rules.d/99-rog-core.rules
|
||||
;;
|
||||
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
19
debian/asus-nb-ctrl.postrm
vendored
19
debian/asus-nb-ctrl.postrm
vendored
@@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
CONF='/etc/rogcore.conf'
|
||||
if [ -f $CONF ]; then
|
||||
rm -rf $CONF
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
0
debian/changelog
vendored
0
debian/changelog
vendored
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
9
|
||||
39
debian/control
vendored
39
debian/control
vendored
@@ -1,39 +0,0 @@
|
||||
Source: asus-nb-ctrl
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Maintainer: Luke Jones <luke@ljones.dev>
|
||||
Build-Depends:
|
||||
debhelper (>=9),
|
||||
cargo,
|
||||
llvm,
|
||||
libclang-dev,
|
||||
libusb-1.0-0-dev,
|
||||
libdbus-1-dev,
|
||||
libudev-1-dev
|
||||
pkg-config
|
||||
Standards-Version: 4.1.1
|
||||
Homepage: https://gitlab.com/asus-linux/asus-nb-ctrl
|
||||
|
||||
Package: asus-nb-ctrl
|
||||
Architecture: amd64
|
||||
Depends:
|
||||
dbus,
|
||||
systemd,
|
||||
libusb-1.0-0,
|
||||
libudev,
|
||||
${misc:Depends},
|
||||
${shlibs:Depends}
|
||||
Description:ASUS ROG Laptop Management
|
||||
Extra support for ASUS laptop models:
|
||||
- GM501
|
||||
- GX502
|
||||
- GX531
|
||||
- G512
|
||||
- G712
|
||||
- GX531
|
||||
- G531
|
||||
- GA14/GA401. You will need kernel patches
|
||||
+ https://lab.retarded.farm/zappel/asus-rog-zephyrus-g14/-/tree/master/kernel_patches
|
||||
- GA15/GA502/GU502 appears to have most things working
|
||||
- GL703(0x1869)
|
||||
- GL553/GL753
|
||||
7
debian/copyright
vendored
7
debian/copyright
vendored
@@ -1,7 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: rog-core
|
||||
Source: https://github.com/flukejones/rog-core
|
||||
|
||||
Files: *
|
||||
Copyright: Copyright 2020 Luke Jones
|
||||
License: MPL-2.0
|
||||
21
debian/rules
vendored
21
debian/rules
vendored
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export VENDORED ?= 1
|
||||
CLEAN ?= 1
|
||||
|
||||
%:
|
||||
dh $@ --with=systemd
|
||||
|
||||
override_dh_auto_build:
|
||||
env CARGO_HOME="$$(pwd)/target/cargo" \
|
||||
dh_auto_build
|
||||
|
||||
override_dh_auto_clean:
|
||||
ifeq ($(CLEAN),1)
|
||||
make clean
|
||||
endif
|
||||
ifeq ($(VENDORED),1)
|
||||
if ! ischroot; then \
|
||||
make vendor; \
|
||||
fi
|
||||
endif
|
||||
1
debian/source/format
vendored
1
debian/source/format
vendored
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
@@ -1,338 +0,0 @@
|
||||
From 61e7ffe80b12db8ebb6033cfe2b70cccaefc07e1 Mon Sep 17 00:00:00 2001
|
||||
From: Luke D Jones <luke@ljones.dev>
|
||||
Date: Sat, 1 Aug 2020 20:33:28 +1200
|
||||
Subject: [PATCH] HID: asus: add support for ASUS N-Key keyboard
|
||||
|
||||
Enable missing functionality of the keyboard found in many ASUS
|
||||
Zephyrus laptops: Fn key combos and hotkeys, keyboard backlight
|
||||
brightness control, and notify asus-wmi to toggle "fan-mode".
|
||||
Two input event codes are added for keyboard LED mode switching
|
||||
prev/next.
|
||||
|
||||
The keyboard has many of the same key outputs as the existing G752
|
||||
keyboard including a few extras, and varies a little between laptop
|
||||
models.
|
||||
|
||||
Additionally the keyboard requires the LED interface to be
|
||||
intitialised before such things as keyboard backlight control work.
|
||||
|
||||
Misc changes in scope: update some hardcoded comparisons to use an
|
||||
available define, change "Mic Toggle" to use a keycode that works.
|
||||
|
||||
Signed-off-by: Luke D Jones <luke@ljones.dev>
|
||||
---
|
||||
drivers/hid/hid-asus.c | 164 ++++++++++++++++++---
|
||||
drivers/hid/hid-ids.h | 1 +
|
||||
include/linux/platform_data/x86/asus-wmi.h | 2 +
|
||||
include/uapi/linux/input-event-codes.h | 7 +
|
||||
4 files changed, 152 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
||||
index e6e4c841fb06..fa9928672110 100644
|
||||
--- a/drivers/hid/hid-asus.c
|
||||
+++ b/drivers/hid/hid-asus.c
|
||||
@@ -26,10 +26,12 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <linux/platform_data/x86/asus-wmi.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
|
||||
#include <linux/power_supply.h>
|
||||
+#include <../drivers/platform/x86/asus-wmi.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@@ -46,6 +48,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
#define FEATURE_KBD_REPORT_ID 0x5a
|
||||
#define FEATURE_KBD_REPORT_SIZE 16
|
||||
+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
||||
+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
||||
|
||||
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
||||
|
||||
@@ -77,6 +81,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_G752_KEYBOARD BIT(8)
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
#define QUIRK_T90CHI BIT(10)
|
||||
+#define QUIRK_ROG_NKEY_KEYBOARD BIT(12)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -257,10 +262,33 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This enables triggering events in asus-wmi
|
||||
+*/
|
||||
+static int asus_wmi_send_event(struct asus_drvdata *drvdat, u8 code)
|
||||
+{
|
||||
+ int err;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
|
||||
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 0) {
|
||||
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
|
||||
(usage->hid & HID_USAGE) != 0x00 &&
|
||||
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
|
||||
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
|
||||
@@ -281,6 +309,20 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
if (drvdata->tp && data[0] == INPUT_REPORT_ID)
|
||||
return asus_report_input(drvdata, data, size);
|
||||
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /*
|
||||
+ * Skip these report ID, the device emits a continuous stream associated
|
||||
+ * with the AURA mode it is in
|
||||
+ */
|
||||
+ if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
|
||||
+ report->id == FEATURE_KBD_LED_REPORT_ID2) {
|
||||
+ return -1;
|
||||
+ /* Fn+F5 "fan" symbol, trigger WMI event to toggle next mode */
|
||||
+ } else if (report->id == FEATURE_KBD_REPORT_ID && data[1] == 0xae) {
|
||||
+ return asus_wmi_send_event(drvdata, 0xae);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -293,7 +335,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
|
||||
+ // The report ID should be set from the incoming buffer due to LED and key
|
||||
+ // interfaces having different pages
|
||||
+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
@@ -346,6 +390,44 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int asus_kbd_led_init(struct hid_device *hdev)
|
||||
+{
|
||||
+ u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
|
||||
+ u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
|
||||
+ 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
|
||||
+ u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
|
||||
+ 0x05, 0x20, 0x31, 0x00, 0x08 };
|
||||
+ int ret;
|
||||
+
|
||||
+ hid_warn(hdev, "Asus initialise N-KEY Device");
|
||||
+ /* The first message is an init start */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init start command: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.0: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.1: %d\n", ret);
|
||||
+
|
||||
+ /* begin second report ID with same data */
|
||||
+ buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+ buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.0: %d\n", ret);
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.1: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -409,19 +491,28 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
- /* Initialize keyboard */
|
||||
- ret = asus_kbd_init(hdev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ /* Initialize keyboard */
|
||||
+ ret = asus_kbd_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Get keyboard functions */
|
||||
- ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ /* Get keyboard functions */
|
||||
+ ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Check for backlight support */
|
||||
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
- return -ENODEV;
|
||||
+ /* Check for backlight support */
|
||||
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /* Initialize keyboard LED interface and Vendor keys on 0x1866 */
|
||||
+ ret = asus_kbd_led_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct asus_kbd_leds),
|
||||
@@ -693,14 +784,14 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
}
|
||||
|
||||
/* ASUS-specific keyboard hotkeys */
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
|
||||
set_bit(EV_REP, hi->input->evbit);
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
|
||||
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
|
||||
- case 0x7c: asus_map_key_clear(KEY_MICMUTE); break;
|
||||
+ case 0x7c: asus_map_key_clear(KEY_F20); break;
|
||||
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
|
||||
case 0xb5: asus_map_key_clear(KEY_CALC); break;
|
||||
@@ -713,16 +804,42 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
/* ROG key */
|
||||
case 0x38: asus_map_key_clear(KEY_PROG1); break;
|
||||
|
||||
- /* Fn+C ASUS Splendid */
|
||||
- case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
+ default:
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+C ASUS Splendid */
|
||||
+ case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
|
||||
- /* Fn+Space Power4Gear Hybrid */
|
||||
- case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
+ /* Fn+Space Power4Gear Hybrid */
|
||||
+ case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
- /* Fn+F5 "fan" symbol on FX503VD */
|
||||
- case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+ /* Fn+F5 "fan" symbol on FX503VD */
|
||||
+ case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* device 0x1866, N-KEY Device specific */
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+Ret "Calc" symbol on device 0x1866, N-KEY Device */
|
||||
+ case 0x92: asus_map_key_clear(KEY_CALC); break;
|
||||
+
|
||||
+ /* Fn+Left Aura mode previous */
|
||||
+ case 0xb2: asus_map_key_clear(KEY_KBDILLUM_MODE_PREV); break;
|
||||
+
|
||||
+ /* Fn+Right Aura mode next */
|
||||
+ case 0xb3: asus_map_key_clear(KEY_KBDILLUM_MODE_NEXT); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
return -1;
|
||||
@@ -1043,6 +1160,9 @@ static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
|
||||
index 1c71a1aa76b2..42c2ca3832e0 100644
|
||||
--- a/drivers/hid/hid-ids.h
|
||||
+++ b/drivers/hid/hid-ids.h
|
||||
@@ -193,6 +193,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index d39fc658c320..10fca778ff9c 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -27,6 +27,8 @@
|
||||
#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
|
||||
#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
|
||||
|
||||
+#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method ?? */
|
||||
+
|
||||
#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
|
||||
|
||||
/* Wireless */
|
||||
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
|
||||
index b6a835d37826..928abcf020d5 100644
|
||||
--- a/include/uapi/linux/input-event-codes.h
|
||||
+++ b/include/uapi/linux/input-event-codes.h
|
||||
@@ -772,6 +772,13 @@
|
||||
#define BTN_TRIGGER_HAPPY39 0x2e6
|
||||
#define BTN_TRIGGER_HAPPY40 0x2e7
|
||||
|
||||
+/*
|
||||
+ * Some keyboards have function keys associated with
|
||||
+ * changing the keyboard backlight modes, e.g, RGB patterns
|
||||
+ */
|
||||
+#define KEY_KBDILLUM_MODE_PREV 0x2ea
|
||||
+#define KEY_KBDILLUM_MODE_NEXT 0x2eb
|
||||
+
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
#define KEY_MIN_INTERESTING KEY_MUTE
|
||||
#define KEY_MAX 0x2ff
|
||||
--
|
||||
2.26.2
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
From 2b70c3daf1bd92f9163efb726e37fb3e0bcc8989 Mon Sep 17 00:00:00 2001
|
||||
From: Luke D Jones <luke@ljones.dev>
|
||||
Date: Thu, 30 Jul 2020 16:51:06 +1200
|
||||
Subject: [PATCH] HID: asus: add support for ASUS N-Key keyboard
|
||||
|
||||
Enable missing functionality of the keyboard found in many ASUS
|
||||
Zephyrus laptops: Fn key combos and hotkeys, keyboard backlight
|
||||
brightness control, and notify asus-wmi to toggle "fan-mode".
|
||||
Two input event codes are added for keyboard LED mode switching
|
||||
prev/next.
|
||||
|
||||
The keyboard has many of the same key outputs as the existing G752
|
||||
keyboard including a few extras, and varies a little between laptop
|
||||
models.
|
||||
|
||||
Additionally the keyboard requires the LED interface to be
|
||||
intitialised before such things as keyboard backlight control work.
|
||||
|
||||
Misc changes in scope: update some hardcoded comparisons to use an
|
||||
available define, change "Mic Toggle" to use a keycode that works.
|
||||
|
||||
Signed-off-by: Luke D Jones <luke@ljones.dev>
|
||||
---
|
||||
drivers/hid/hid-asus.c | 166 ++++++++++++++++++---
|
||||
drivers/hid/hid-ids.h | 1 +
|
||||
include/linux/platform_data/x86/asus-wmi.h | 2 +
|
||||
include/uapi/linux/input-event-codes.h | 7 +
|
||||
4 files changed, 153 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
||||
index c183caf89d49..5dfca90cd616 100644
|
||||
--- a/drivers/hid/hid-asus.c
|
||||
+++ b/drivers/hid/hid-asus.c
|
||||
@@ -26,10 +26,12 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <linux/platform_data/x86/asus-wmi.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
|
||||
#include <linux/power_supply.h>
|
||||
+#include <../drivers/platform/x86/asus-wmi.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@@ -48,6 +50,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
#define FEATURE_KBD_REPORT_ID 0x5a
|
||||
#define FEATURE_KBD_REPORT_SIZE 16
|
||||
+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
||||
+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
||||
|
||||
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
||||
|
||||
@@ -80,6 +84,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
#define QUIRK_T90CHI BIT(10)
|
||||
#define QUIRK_MEDION_E1239T BIT(11)
|
||||
+#define QUIRK_ROG_NKEY_KEYBOARD BIT(12)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -305,10 +310,33 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This enables triggering events in asus-wmi
|
||||
+ */
|
||||
+static int asus_wmi_send_event(struct asus_drvdata *drvdat, u8 code)
|
||||
+{
|
||||
+ int err;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
|
||||
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 0) {
|
||||
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
|
||||
(usage->hid & HID_USAGE) != 0x00 &&
|
||||
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
|
||||
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
|
||||
@@ -332,6 +360,20 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
if (drvdata->quirks & QUIRK_MEDION_E1239T)
|
||||
return asus_e1239t_event(drvdata, data, size);
|
||||
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /*
|
||||
+ * Skip these report ID, the device emits a continuous stream associated
|
||||
+ * with the AURA mode it is in
|
||||
+ */
|
||||
+ if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
|
||||
+ report->id == FEATURE_KBD_LED_REPORT_ID2) {
|
||||
+ return -1;
|
||||
+ /* Fn+F5 "fan" symbol, trigger WMI event to toggle next mode */
|
||||
+ } else if (report->id == FEATURE_KBD_REPORT_ID && data[1] == 0xae) {
|
||||
+ return asus_wmi_send_event(drvdata, 0xae);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -344,7 +386,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
|
||||
+ // The report ID should be set from the incoming buffer due to LED and key
|
||||
+ // interfaces having different pages
|
||||
+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
@@ -397,6 +441,44 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int asus_kbd_led_init(struct hid_device *hdev)
|
||||
+{
|
||||
+ u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
|
||||
+ u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
|
||||
+ 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
|
||||
+ u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
|
||||
+ 0x05, 0x20, 0x31, 0x00, 0x08 };
|
||||
+ int ret;
|
||||
+
|
||||
+ hid_warn(hdev, "Asus initialise N-KEY Device");
|
||||
+ /* The first message is an init start */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init start command: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.0: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.1: %d\n", ret);
|
||||
+
|
||||
+ /* begin second report ID with same data */
|
||||
+ buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+ buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.0: %d\n", ret);
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.1: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -460,19 +542,28 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
- /* Initialize keyboard */
|
||||
- ret = asus_kbd_init(hdev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ /* Initialize keyboard */
|
||||
+ ret = asus_kbd_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Get keyboard functions */
|
||||
- ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ /* Get keyboard functions */
|
||||
+ ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Check for backlight support */
|
||||
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
- return -ENODEV;
|
||||
+ /* Check for backlight support */
|
||||
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /* Initialize keyboard LED interface and Vendor keys on 0x1866 */
|
||||
+ ret = asus_kbd_led_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct asus_kbd_leds),
|
||||
@@ -751,14 +842,14 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)))
|
||||
return -1;
|
||||
|
||||
- /* ASUS-specific keyboard hotkeys */
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
|
||||
+ /* ASUS-specific keyboard hotkeys and led backlight */
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
|
||||
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
|
||||
- case 0x7c: asus_map_key_clear(KEY_MICMUTE); break;
|
||||
+ case 0x7c: asus_map_key_clear(KEY_F20); break;
|
||||
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
|
||||
case 0xb5: asus_map_key_clear(KEY_CALC); break;
|
||||
@@ -771,16 +862,42 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
/* ROG key */
|
||||
case 0x38: asus_map_key_clear(KEY_PROG1); break;
|
||||
|
||||
- /* Fn+C ASUS Splendid */
|
||||
- case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
+ default:
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+C ASUS Splendid */
|
||||
+ case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
|
||||
- /* Fn+Space Power4Gear Hybrid */
|
||||
- case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
+ /* Fn+Space Power4Gear Hybrid */
|
||||
+ case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
- /* Fn+F5 "fan" symbol on FX503VD */
|
||||
- case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+ /* Fn+F5 "fan" symbol on FX503VD */
|
||||
+ case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* device 0x1866, N-KEY Device specific */
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+Ret "Calc" symbol on device 0x1866, N-KEY Device */
|
||||
+ case 0x92: asus_map_key_clear(KEY_CALC); break;
|
||||
+
|
||||
+ /* Fn+Left Aura mode previous */
|
||||
+ case 0xb2: asus_map_key_clear(KEY_KBDILLUM_MODE_PREV); break;
|
||||
+
|
||||
+ /* Fn+Right Aura mode next */
|
||||
+ case 0xb3: asus_map_key_clear(KEY_KBDILLUM_MODE_NEXT); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
return -1;
|
||||
@@ -1126,6 +1243,9 @@ static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
|
||||
index 6f370e020feb..c9f930ddcfd7 100644
|
||||
--- a/drivers/hid/hid-ids.h
|
||||
+++ b/drivers/hid/hid-ids.h
|
||||
@@ -190,6 +190,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index 897b8332a39f..05253cfe786c 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -27,6 +27,8 @@
|
||||
#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
|
||||
#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
|
||||
|
||||
+#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method ?? */
|
||||
+
|
||||
#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
|
||||
|
||||
/* Wireless */
|
||||
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
|
||||
index 0c2e27d28e0a..ca59f7d7a25e 100644
|
||||
--- a/include/uapi/linux/input-event-codes.h
|
||||
+++ b/include/uapi/linux/input-event-codes.h
|
||||
@@ -772,6 +772,13 @@
|
||||
#define BTN_TRIGGER_HAPPY39 0x2e6
|
||||
#define BTN_TRIGGER_HAPPY40 0x2e7
|
||||
|
||||
+/*
|
||||
+ * Some keyboards have function keys associated with
|
||||
+ * changing the keyboard backlight modes, e.g, RGB patterns
|
||||
+ */
|
||||
+#define KEY_KBDILLUM_MODE_PREV 0x2ea
|
||||
+#define KEY_KBDILLUM_MODE_NEXT 0x2eb
|
||||
+
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
#define KEY_MIN_INTERESTING KEY_MUTE
|
||||
#define KEY_MAX 0x2ff
|
||||
--
|
||||
2.26.2
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
---
|
||||
.../ABI/testing/sysfs-platform-asus-wmi | 10 ++
|
||||
drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++
|
||||
include/linux/platform_data/x86/asus-wmi.h | 1 +
|
||||
3 files changed, 124 insertions(+)
|
||||
|
||||
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
index 9e99f2909612..1efac0ddb417 100644
|
||||
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
@@ -46,3 +46,13 @@ Description:
|
||||
* 0 - normal,
|
||||
* 1 - overboost,
|
||||
* 2 - silent
|
||||
+
|
||||
+What: /sys/devices/platform/<platform>/throttle_thermal_policy
|
||||
+Date: Dec 2019
|
||||
+KernelVersion: 5.6
|
||||
+Contact: "Leonid Maksymchuk" <leonmaxx@gmail.com>
|
||||
+Description:
|
||||
+ Throttle thermal policy mode:
|
||||
+ * 0 - default,
|
||||
+ * 1 - overboost,
|
||||
+ * 2 - silent
|
||||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
||||
index 821b08e01635..f10ec9d745e5 100644
|
||||
--- a/drivers/platform/x86/asus-wmi.c
|
||||
+++ b/drivers/platform/x86/asus-wmi.c
|
||||
@@ -61,6 +61,7 @@ MODULE_LICENSE("GPL");
|
||||
#define NOTIFY_KBD_BRTDWN 0xc5
|
||||
#define NOTIFY_KBD_BRTTOGGLE 0xc7
|
||||
#define NOTIFY_KBD_FBM 0x99
|
||||
+#define NOTIFY_KBD_TTP 0xae
|
||||
|
||||
#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
|
||||
|
||||
@@ -81,6 +82,10 @@ MODULE_LICENSE("GPL");
|
||||
#define ASUS_FAN_BOOST_MODE_SILENT_MASK 0x02
|
||||
#define ASUS_FAN_BOOST_MODES_MASK 0x03
|
||||
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT 0
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2
|
||||
+
|
||||
#define USB_INTEL_XUSB2PR 0xD0
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
|
||||
|
||||
@@ -198,6 +203,9 @@ struct asus_wmi {
|
||||
u8 fan_boost_mode_mask;
|
||||
u8 fan_boost_mode;
|
||||
|
||||
+ bool throttle_thermal_policy_available;
|
||||
+ u8 throttle_thermal_policy_mode;
|
||||
+
|
||||
// The RSOC controls the maximum charging percentage.
|
||||
bool battery_rsoc_available;
|
||||
|
||||
@@ -1724,6 +1732,98 @@ static ssize_t fan_boost_mode_store(struct device *dev,
|
||||
// Fan boost mode: 0 - normal, 1 - overboost, 2 - silent
|
||||
static DEVICE_ATTR_RW(fan_boost_mode);
|
||||
|
||||
+/* Throttle thermal policy ****************************************************/
|
||||
+
|
||||
+static int throttle_thermal_policy_check_present(struct asus_wmi *asus)
|
||||
+{
|
||||
+ u32 result;
|
||||
+ int err;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_available = false;
|
||||
+
|
||||
+ err = asus_wmi_get_devstate(asus,
|
||||
+ ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
+ &result);
|
||||
+ if (err) {
|
||||
+ if (err == -ENODEV)
|
||||
+ return 0;
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
|
||||
+ asus->throttle_thermal_policy_available = true;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
+{
|
||||
+ int err;
|
||||
+ u8 value;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ value = asus->throttle_thermal_policy_mode;
|
||||
+
|
||||
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
+ value, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to set throttle thermal policy: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 1) {
|
||||
+ pr_warn("Failed to set throttle thermal policy (retval): 0x%x\n",
|
||||
+ retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
|
||||
+{
|
||||
+ u8 new_mode = asus->throttle_thermal_policy_mode + 1;
|
||||
+
|
||||
+ if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
+ new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = new_mode;
|
||||
+ return throttle_thermal_policy_write(asus);
|
||||
+}
|
||||
+
|
||||
+static ssize_t throttle_thermal_policy_show(struct device *dev,
|
||||
+ struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
+ u8 mode = asus->throttle_thermal_policy_mode;
|
||||
+
|
||||
+ return scnprintf(buf, PAGE_SIZE, "%d\n", mode);
|
||||
+}
|
||||
+
|
||||
+static ssize_t throttle_thermal_policy_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t count)
|
||||
+{
|
||||
+ int result;
|
||||
+ u8 new_mode;
|
||||
+ struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
+
|
||||
+ result = kstrtou8(buf, 10, &new_mode);
|
||||
+ if (result < 0)
|
||||
+ return result;
|
||||
+
|
||||
+ if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = new_mode;
|
||||
+ throttle_thermal_policy_write(asus);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
|
||||
+static DEVICE_ATTR_RW(throttle_thermal_policy);
|
||||
+
|
||||
/* Backlight ******************************************************************/
|
||||
|
||||
static int read_backlight_power(struct asus_wmi *asus)
|
||||
@@ -2005,6 +2105,11 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) {
|
||||
+ throttle_thermal_policy_switch_next(asus);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
|
||||
return;
|
||||
|
||||
@@ -2155,6 +2260,7 @@ static struct attribute *platform_attributes[] = {
|
||||
&dev_attr_lid_resume.attr,
|
||||
&dev_attr_als_enable.attr,
|
||||
&dev_attr_fan_boost_mode.attr,
|
||||
+ &dev_attr_throttle_thermal_policy.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -2178,6 +2284,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
devid = ASUS_WMI_DEVID_ALS_ENABLE;
|
||||
else if (attr == &dev_attr_fan_boost_mode.attr)
|
||||
ok = asus->fan_boost_mode_available;
|
||||
+ else if (attr == &dev_attr_throttle_thermal_policy.attr)
|
||||
+ ok = asus->throttle_thermal_policy_available;
|
||||
|
||||
if (devid != -1)
|
||||
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
|
||||
@@ -2437,6 +2545,10 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto fail_fan_boost_mode;
|
||||
|
||||
+ err = throttle_thermal_policy_check_present(asus);
|
||||
+ if (err)
|
||||
+ goto fail_throttle_thermal_policy;
|
||||
+
|
||||
err = asus_wmi_sysfs_init(asus->platform_device);
|
||||
if (err)
|
||||
goto fail_sysfs;
|
||||
@@ -2521,6 +2633,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
fail_input:
|
||||
asus_wmi_sysfs_exit(asus->platform_device);
|
||||
fail_sysfs:
|
||||
+fail_throttle_thermal_policy:
|
||||
fail_fan_boost_mode:
|
||||
fail_platform:
|
||||
kfree(asus);
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index 60249e22e844..d39fc658c320 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -58,6 +58,7 @@
|
||||
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
|
||||
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
|
||||
#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018
|
||||
+#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
|
||||
|
||||
/* Misc */
|
||||
#define ASUS_WMI_DEVID_CAMERA 0x00060013
|
||||
--
|
||||
2.24.0
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
drivers/platform/x86/asus-wmi.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
||||
index f10ec9d745e5..469f1a852719 100644
|
||||
--- a/drivers/platform/x86/asus-wmi.c
|
||||
+++ b/drivers/platform/x86/asus-wmi.c
|
||||
@@ -1780,6 +1780,15 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
|
||||
+{
|
||||
+ if (!asus->throttle_thermal_policy_available)
|
||||
+ return 0;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
+ return throttle_thermal_policy_write(asus);
|
||||
+}
|
||||
+
|
||||
static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
|
||||
{
|
||||
u8 new_mode = asus->throttle_thermal_policy_mode + 1;
|
||||
@@ -2548,6 +2557,8 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
err = throttle_thermal_policy_check_present(asus);
|
||||
if (err)
|
||||
goto fail_throttle_thermal_policy;
|
||||
+ else
|
||||
+ throttle_thermal_policy_set_default(asus);
|
||||
|
||||
err = asus_wmi_sysfs_init(asus->platform_device);
|
||||
if (err)
|
||||
--
|
||||
2.24.0
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
[root@rog tmp]# cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
|
||||
[root@rog tmp]# iasl -d dsdt.dat
|
||||
|
||||
Intel ACPI Component Architecture
|
||||
ASL+ Optimizing Compiler/Disassembler version 20200717
|
||||
Copyright (c) 2000 - 2020 Intel Corporation
|
||||
|
||||
File appears to be binary: found 97272 non-ASCII characters, disassembling
|
||||
Binary file appears to be a valid ACPI table, disassembling
|
||||
Input file dsdt.dat, Length 0x45955 (285013) bytes
|
||||
ACPI: DSDT 0x0000000000000000 045955 (v02 _ASUS_ Notebook 01072009 INTL 20160527)
|
||||
Pass 1 parse of [DSDT]
|
||||
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
Pass 2 parse of [DSDT]
|
||||
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions)
|
||||
|
||||
Parsing completed
|
||||
Warning - Emitting ASL code "External (ESPC)"
|
||||
This is a conflicting declaration with some other declaration within the ASL code.
|
||||
This external declaration may need to be deleted in order to recompile the dsl file.
|
||||
|
||||
Warning - Emitting ASL code "External (PSON)"
|
||||
This is a conflicting declaration with some other declaration within the ASL code.
|
||||
This external declaration may need to be deleted in order to recompile the dsl file.
|
||||
|
||||
Disassembly completed
|
||||
ASL Output: dsdt.dsl - 2006627 bytes
|
||||
[root@rog tmp]#
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
cat /sys/class/dmi/id/product_name:ROG Strix G512LU_G512LU
|
||||
cat /sys/class/dmi/id/product_family: ROG Strix
|
||||
cat /sys/class/dmi/id/board_name:G512LU
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
001:002:002:DESCRIPTOR 1593779817.131018
|
||||
06 31 FF 09 76 A1 01 85 5A 19 00 2A FF 00 15 00
|
||||
26 FF 00 75 08 95 05 81 00 19 00 2A FF 00 15 00
|
||||
26 FF 00 75 08 95 3F B1 00 C0 05 0C 09 01 A1 01
|
||||
85 02 19 00 2A 3C 02 15 00 26 3C 02 75 10 95 02
|
||||
81 00 C0 06 31 FF 09 79 A1 01 85 5D 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 1F 81 00 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 3F 91 00 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 3F B1 00 C0 06 31 FF
|
||||
09 80 A1 01 85 5E 19 00 2A FF 00 15 00 26 FF 00
|
||||
75 08 95 05 81 00 19 00 2A FF 00 15 00 26 FF 00
|
||||
75 08 95 3F B1 00 C0
|
||||
|
||||
001:002:001:DESCRIPTOR 1593779817.133253
|
||||
05 01 09 06 A1 01 85 09 75 01 95 08 05 07 19 E0
|
||||
29 E7 15 00 25 01 81 02 95 08 75 01 81 03 95 05
|
||||
75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03
|
||||
95 F0 75 01 05 07 19 00 29 EF 15 00 25 01 81 02
|
||||
C0
|
||||
|
||||
001:002:000:DESCRIPTOR 1593779817.134955
|
||||
05 01 09 06 A1 01 85 01 75 01 95 08 05 07 19 E0
|
||||
29 E7 15 00 25 01 81 02 95 01 75 08 81 03 95 05
|
||||
75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03
|
||||
95 06 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00
|
||||
81 00 95 C0 75 01 05 07 19 00 29 EF 15 00 25 01
|
||||
81 02 C0
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Product name: ROG Strix G531GT_G531GT
|
||||
Product family: ROG Strix
|
||||
Board name: G531GT
|
||||
|
||||
lsusb | grep 0b05: Bus 001 Device 004: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
1b00a00964188090ffff00000000090000020002000401400000005dbc00010101001000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000 ROGKEY
|
||||
1b006079b90e8090ffff00000000090000020002000401400000005dbc00010101601000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ENTER
|
||||
1b00e014ee088090ffff00000000090000020002000401400000005dbc00010101701000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000 RSHIFT
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,130 +0,0 @@
|
||||
|
||||
Bus 001 Device 004: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 0
|
||||
bDeviceSubClass 0
|
||||
bDeviceProtocol 0
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x0b05 ASUSTek Computer, Inc.
|
||||
idProduct 0x1866
|
||||
bcdDevice 0.02
|
||||
iManufacturer 1 ASUSTeK Computer Inc.
|
||||
iProduct 2 N-KEY Device
|
||||
iSerial 0
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 0x005b
|
||||
bNumInterfaces 3
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0xe0
|
||||
Self Powered
|
||||
Remote Wakeup
|
||||
MaxPower 100mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 1
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 1 Boot Interface Subclass
|
||||
bInterfaceProtocol 1 Keyboard
|
||||
iInterface 3 (error)
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.10
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 67
|
||||
Report Descriptors:
|
||||
** UNAVAILABLE **
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x81 EP 1 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 1
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 1
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 1 Boot Interface Subclass
|
||||
bInterfaceProtocol 1 Keyboard
|
||||
iInterface 1 ASUSTeK Computer Inc.
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.10
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 65
|
||||
Report Descriptors:
|
||||
** UNAVAILABLE **
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x82 EP 2 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 2
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 1 Boot Interface Subclass
|
||||
bInterfaceProtocol 1 Keyboard
|
||||
iInterface 1 ASUSTeK Computer Inc.
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.10
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 167
|
||||
Report Descriptors:
|
||||
** UNAVAILABLE **
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x83 EP 3 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x04 EP 4 OUT
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 1
|
||||
Device Status: 0x0001
|
||||
Self Powered
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user