Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8384c55c3 | ||
|
|
dfe1f02101 | ||
|
|
7c2fb0be81 | ||
|
|
b05f680650 | ||
|
|
2a9a436f9c | ||
|
|
0d6faf3fda | ||
|
|
aede000218 | ||
|
|
176ab0a639 | ||
|
|
4efb2caa56 | ||
|
|
6f81f86483 | ||
|
|
b64b8a38e4 | ||
|
|
ff56170ac5 | ||
|
|
733f1f827e | ||
|
|
ac903a05da | ||
|
|
838e6f789b | ||
|
|
d462393e8b | ||
|
|
e98cf8d50b | ||
|
|
eb173fc9dc | ||
|
|
50756046cf | ||
|
|
629bdc2213 | ||
|
|
39bbe33831 | ||
|
|
00bd556d7a | ||
|
|
12061ea9df | ||
|
|
580ed72e73 | ||
|
|
c01f0892a5 | ||
|
|
0fed34b12e | ||
|
|
0af68baf7b | ||
|
|
161e3c4d3b | ||
|
|
4720af2cb8 | ||
|
|
06d37aa009 | ||
|
|
c6fa860b2e | ||
|
|
4fe9ab70e5 | ||
|
|
920e4e86f5 | ||
|
|
720dc0c177 | ||
|
|
b3a555cab9 | ||
|
|
cf13b4f71b | ||
|
|
cd0b9fe350 | ||
|
|
82900f4645 | ||
|
|
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 |
@@ -13,12 +13,12 @@ test:
|
||||
|
||||
build:
|
||||
only:
|
||||
- next
|
||||
- main
|
||||
script:
|
||||
- make && make vendor
|
||||
artifacts:
|
||||
paths:
|
||||
- vendor-$(grep -P 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2).tar.xz
|
||||
- vendor_asus-nb-ctrl_*.tar.xz
|
||||
- cargo-config
|
||||
|
||||
variables:
|
||||
|
||||
134
CHANGELOG.md
@@ -6,6 +6,140 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
# [3.1.2] - 2021-03-10
|
||||
### Changed
|
||||
- Test and create /etc/X11/xorg.conf.d/ if it doesn't exist
|
||||
- Hotfix to better report module issues
|
||||
|
||||
# [3.1.1] - 2021-03-10
|
||||
### Changed
|
||||
- Add missing nvidia module nvidia_uvm to gfx ctrl list
|
||||
|
||||
# [3.1.0] - 2021-03-09
|
||||
### Added
|
||||
- GU502LU led-modes
|
||||
### Changed
|
||||
- Graphics switching is now rebootless, the daemon will now restart the
|
||||
display-manager to switch modes instead. Caveats are:
|
||||
+ There is no confirmation from the daemon, the program issuing the command
|
||||
must confirm the request.
|
||||
+ systemd only
|
||||
- Laptops with dedicated Nvidia mode:
|
||||
+ You still must reboot for the bios to switch modes
|
||||
+ On boot if dedicated mode is active then asusd will update the required configs
|
||||
to put display-manager in nvidia mode
|
||||
|
||||
# [3.0.0] - 2021-02-22
|
||||
### Added
|
||||
- G531GD led modes
|
||||
|
||||
# [3.0.0] - 2021-02-14
|
||||
### Changed
|
||||
- Write set+apply after each array in multizone
|
||||
- Remove misc bad logic
|
||||
- Use same code path as 0x1866 device to configure led support for 0x1854 device
|
||||
- Remove duplicate code
|
||||
- Set correct speeds for multizone
|
||||
- Remove dbus crate in favour of zbus. This removes the external dbus lib requirement.
|
||||
- Huge internal refactor
|
||||
- BREAKING CHANGE: Anime code refactor. DBUS method names have changed
|
||||
- Cleanup fan and cpu control + configs
|
||||
|
||||
# [2.2.2] - 2021-01-31
|
||||
### Changed
|
||||
- Fix for dedicated gfx capable laptops in integrated mode
|
||||
- Fix for 0x1854 device
|
||||
|
||||
# [2.2.1] - 2021-01-27
|
||||
### Added
|
||||
- Add ROG Zephyrus M15 LED config
|
||||
### Changed
|
||||
- Bugfixes
|
||||
- Fix reboot/restartx status for GFX switching
|
||||
- Update readme
|
||||
- Change CLI arg tag for fan modes
|
||||
- Make dracut include the nvidia modules in initramfs
|
||||
|
||||
# [2.2.0] - 2021-01-26
|
||||
### Added
|
||||
- Dbus command to fetch all supported functions of the laptop. That is, all the
|
||||
functions that asusd supports for the currently running laptop.
|
||||
- Bios setting toggles for:
|
||||
+ Dedicated gfx toggle (support depends on the laptop)
|
||||
+ Bios boot POST sound toggle
|
||||
### Changed
|
||||
- added config option for dedicated gfx mode on laptops with it to enable
|
||||
switching directly to dedicated using `asusctl graphics -m nvidia`
|
||||
|
||||
# [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
|
||||
|
||||
1076
Cargo.lock
generated
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["asus-nb-ctrl", "asus-nb"]
|
||||
members = ["asusctl", "asus-notify", "daemon", "rog-types", "rog-dbus"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -13,4 +13,4 @@ opt-level = 1
|
||||
|
||||
[profile.bench]
|
||||
debug = false
|
||||
opt-level = 3
|
||||
opt-level = 3
|
||||
|
||||
79
Makefile
@@ -1,33 +1,37 @@
|
||||
prefix ?= /usr
|
||||
sysconfdir ?= /etc
|
||||
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' daemon/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
|
||||
VERSION:=$(shell grep -P 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2)
|
||||
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
|
||||
@@ -35,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
|
||||
@@ -59,12 +80,14 @@ vendor:
|
||||
echo 'directory = "vendor"' >> .cargo/config
|
||||
mv .cargo/config ./cargo-config
|
||||
rm -rf .cargo
|
||||
tar pcfJ vendor-$(VERSION).tar.xz vendor
|
||||
tar pcfJ vendor_asus-nb-ctrl_$(VERSION).tar.xz vendor
|
||||
rm -rf vendor
|
||||
|
||||
target/release/$(BIN_D): $(SRC)
|
||||
build:
|
||||
ifeq ($(VENDORED),1)
|
||||
@echo "version = $(VERSION)"
|
||||
tar pxf vendor-$(VERSION).tar.xz
|
||||
tar pxf vendor_asus-nb-ctrl_$(VERSION).tar.xz
|
||||
endif
|
||||
cargo build $(ARGS)
|
||||
|
||||
.PHONY: all clean distclean install uninstall update build
|
||||
|
||||
286
README.md
@@ -1,10 +1,21 @@
|
||||
# ASUS NB Ctrl
|
||||
|
||||
[](https://www.paypal.com/donate/?hosted_button_id=4V2DEPS7K6APC) - [Asus Linux Website](https://asus-linux.org/)
|
||||
|
||||
`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. 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/).
|
||||
This app is developed and tested on fedora only. Support is not provided for Arch or Arch based distros.
|
||||
|
||||
**NOTICE:**
|
||||
The following is *not* required for 5.11 kernel versions, as this version includes
|
||||
all the required patches.
|
||||
---
|
||||
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:
|
||||
|
||||
@@ -12,73 +23,91 @@ 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
|
||||
- [X] Toggle bios setting for boot/POST sound
|
||||
- [X] Toggle bios setting for "dedicated gfx" mode on supported laptops (g-sync)
|
||||
|
||||
**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,`. Additionally there is an extra setting
|
||||
for laptops capable of g-sync dedicated gfx mode to enable the graphics switching
|
||||
to switch on dedicated gfx for "nvidia" mode.
|
||||
|
||||
**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. This is espeically
|
||||
true if you manually installed the nvidia drivers.
|
||||
|
||||
```
|
||||
# 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
|
||||
@@ -97,25 +126,29 @@ 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 libclang-dev libudev-dev`
|
||||
|
||||
**fedora:** `dnf install clang-devel systemd-devel`
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -123,6 +156,8 @@ Packaging and auto-builds are available [here](https://build.opensuse.org/packag
|
||||
|
||||
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.
|
||||
@@ -136,32 +171,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:
|
||||
|
||||
@@ -182,60 +221,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:
|
||||
@@ -249,27 +234,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
@@ -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
|
||||
1016
asus-nb-ctrl/Cargo.lock
generated
@@ -1,128 +0,0 @@
|
||||
use asus_nb::aura_modes::AuraModes;
|
||||
use log::{error, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
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,
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
let mut buf = String::new();
|
||||
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||
if l == 0 {
|
||||
self = Config::create_default(&mut file, &supported_led_modes);
|
||||
} else {
|
||||
self = serde_json::from_str(&buf).unwrap_or_else(|_| {
|
||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
||||
Config::create_default(&mut file, &supported_led_modes)
|
||||
});
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for n in supported_led_modes {
|
||||
c.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
}
|
||||
|
||||
// Should be okay to unwrap this as is since it is a Default
|
||||
let json = serde_json::to_string_pretty(&c).unwrap();
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
||||
c
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
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();
|
||||
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||
if l == 0 {
|
||||
warn!("File is empty {}", CONFIG_PATH);
|
||||
} else {
|
||||
let x: Config = serde_json::from_str(&buf)
|
||||
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
|
||||
*self = 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");
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||
}
|
||||
|
||||
pub fn set_mode_data(&mut self, mode: AuraModes) {
|
||||
let byte: u8 = (&mode).into();
|
||||
for (index, n) in self.kbd_backlight_modes.iter().enumerate() {
|
||||
if byte == u8::from(n) {
|
||||
// Consume it, OMNOMNOMNOM
|
||||
self.kbd_backlight_modes[index] = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_led_mode_data(&self, num: u8) -> Option<&AuraModes> {
|
||||
for mode in &self.kbd_backlight_modes {
|
||||
if u8::from(mode) == num {
|
||||
return Some(mode);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct FanModeProfile {
|
||||
pub normal: CPUSettings,
|
||||
pub boost: CPUSettings,
|
||||
pub silent: CPUSettings,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CPUSettings {
|
||||
pub min_percentage: u8,
|
||||
pub max_percentage: u8,
|
||||
pub no_turbo: bool,
|
||||
}
|
||||
|
||||
impl Default for CPUSettings {
|
||||
fn default() -> Self {
|
||||
CPUSettings {
|
||||
min_percentage: 0,
|
||||
max_percentage: 100,
|
||||
no_turbo: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
const INIT_STR: &str = "ASUS Tech.Inc.";
|
||||
const PACKET_SIZE: usize = 640;
|
||||
|
||||
// Only these two packets must be 17 bytes
|
||||
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;
|
||||
|
||||
use crate::config::Config;
|
||||
use asus_nb::error::AuraError;
|
||||
use log::{error, info, warn};
|
||||
use rusb::{Device, DeviceHandle};
|
||||
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;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum AnimatrixCommand {
|
||||
Apply,
|
||||
Set,
|
||||
WriteImage(Vec<Vec<u8>>),
|
||||
//ReloadLast,
|
||||
}
|
||||
|
||||
pub struct CtrlAnimeDisplay {
|
||||
handle: DeviceHandle<rusb::GlobalContext>,
|
||||
initialised: bool,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlAnimeDisplay {
|
||||
type A = Vec<Vec<u8>>;
|
||||
|
||||
/// 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));
|
||||
}
|
||||
})]
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, _: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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 mut device = device.open()?;
|
||||
device.reset()?;
|
||||
|
||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||
error!("Auto-detach kernel driver failed: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
device.claim_interface(0).map_err(|err| {
|
||||
error!("Could not claim device interface: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
info!("Device has an AniMe Matrix display");
|
||||
Ok(CtrlAnimeDisplay {
|
||||
handle: device,
|
||||
initialised: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
let device_desc = device.device_descriptor()?;
|
||||
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
||||
return Ok(device);
|
||||
}
|
||||
}
|
||||
Err(rusb::Error::NoDevice)
|
||||
}
|
||||
|
||||
pub async fn do_command(&mut self, command: AnimatrixCommand) -> Result<(), AuraError> {
|
||||
if !self.initialised {
|
||||
self.do_initialization().await?
|
||||
}
|
||||
|
||||
match command {
|
||||
AnimatrixCommand::WriteImage(effect) => self.write_image(effect).await?,
|
||||
AnimatrixCommand::Set => self.do_set().await?,
|
||||
AnimatrixCommand::Apply => self.do_apply().await?,
|
||||
//AnimatrixCommand::ReloadLast => self.reload_last_builtin(&config).await?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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();
|
||||
match self.handle.write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
0x35e, // value
|
||||
0x00, // index
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
) {
|
||||
Ok(_) => {
|
||||
println!(
|
||||
"{:?}",
|
||||
std::time::Instant::now().duration_since(prev).as_micros()
|
||||
);
|
||||
}
|
||||
Err(err) => match err {
|
||||
rusb::Error::Timeout => {}
|
||||
_ => error!("Failed to write to led interrupt: {:?}", err),
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// - Write pane 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
|
||||
/// - Write pane 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
|
||||
///
|
||||
/// Where led brightness is 0..255, low to high
|
||||
#[inline]
|
||||
async fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||
for row in image.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
}
|
||||
self.do_flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async 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?;
|
||||
|
||||
// clear the init array and write other init message
|
||||
for ch in init.iter_mut() {
|
||||
*ch = 0;
|
||||
}
|
||||
init[0] = DEV_PAGE; // write it to be sure?
|
||||
init[1] = INIT;
|
||||
|
||||
self.write_bytes(&init).await?;
|
||||
self.initialised = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async 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?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_set(&mut self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = SET;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = 0x80;
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async 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?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
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;
|
||||
|
||||
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
|
||||
|
||||
pub struct CtrlCharge {
|
||||
path: &'static str,
|
||||
}
|
||||
|
||||
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!("charge_limit: {:?}", err));
|
||||
}
|
||||
})]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlCharge {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let path = CtrlCharge::get_battery_path()?;
|
||||
info!("Device has battery charge threshold control");
|
||||
Ok(CtrlCharge { path })
|
||||
}
|
||||
|
||||
fn get_battery_path() -> Result<&'static str, std::io::Error> {
|
||||
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",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_charge_limit(
|
||||
&self,
|
||||
limit: u8,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if limit < 20 || limit > 100 {
|
||||
warn!(
|
||||
"Unable to set battery charge limit, must be between 20-100: requested {}",
|
||||
limit
|
||||
);
|
||||
}
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to open battery charge limit path: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
file.write_all(limit.to_string().as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", BAT_CHARGE_PATH, err));
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
config.read();
|
||||
config.bat_charge_limit = limit;
|
||||
config.write();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
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;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlFanAndCPU {
|
||||
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<()>> {
|
||||
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));
|
||||
}
|
||||
}),
|
||||
// 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!("fan_ctrl: {:?}", 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)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlFanAndCPU {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let path = CtrlFanAndCPU::get_fan_path()?;
|
||||
info!("Device has thermal throttle control");
|
||||
Ok(CtrlFanAndCPU { path })
|
||||
}
|
||||
|
||||
fn get_fan_path() -> Result<&'static str, std::io::Error> {
|
||||
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",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
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)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pstate_for_fan_mode(
|
||||
&self,
|
||||
mode: FanLevel,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// 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
|
||||
);
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FanLevel {
|
||||
Normal,
|
||||
Boost,
|
||||
Silent,
|
||||
}
|
||||
|
||||
impl FromStr for FanLevel {
|
||||
type Err = RogError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, RogError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"normal" => Ok(FanLevel::Normal),
|
||||
"boost" => Ok(FanLevel::Boost),
|
||||
"silent" => Ok(FanLevel::Silent),
|
||||
_ => Err(RogError::ParseFanLevel),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
// Only these two packets must be 17 bytes
|
||||
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};
|
||||
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;
|
||||
|
||||
pub struct CtrlKbdBacklight {
|
||||
led_node: String,
|
||||
kbd_node: String,
|
||||
bright_node: String,
|
||||
supported_modes: Vec<u8>,
|
||||
flip_effect_write: bool,
|
||||
}
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlKbdBacklight {
|
||||
type A = AuraModes;
|
||||
|
||||
/// 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();
|
||||
|
||||
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 {
|
||||
AuraModes::PerKey(_) => {
|
||||
lock.do_command(command, &mut config)
|
||||
.await
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
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!("led_ctrl: {:?}", err));
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlKbdBacklight {
|
||||
#[inline]
|
||||
pub fn new(id_product: &str, supported_modes: Vec<u8>) -> Result<Self, std::io::Error> {
|
||||
Ok(CtrlKbdBacklight {
|
||||
led_node: Self::get_node_failover(id_product, Self::scan_led_node)?,
|
||||
kbd_node: Self::get_node_failover(id_product, Self::scan_kbd_node)?,
|
||||
// brightness node path should always be constant but this *might* change?
|
||||
bright_node: "/sys/class/leds/asus::kbd_backlight/brightness".to_string(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_node_failover(id_product: &str, fun: fn(&str) -> Result<String, std::io::Error>) -> Result<String, std::io::Error> {
|
||||
for n in 0..2 {
|
||||
match fun(id_product) {
|
||||
Ok(o) => return Ok(o),
|
||||
Err(e) => {
|
||||
if n > 0 {
|
||||
warn!("Looking for node: {}", e.to_string());
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shouldn't be possible to reach this...
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"node not found",
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
|
||||
fn scan_led_node(id_product: &str) -> Result<String, std::io::Error> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
enumerator.match_subsystem("hidraw")?;
|
||||
|
||||
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
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS LED device node not found",
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
for device in enumerator.scan_devices()? {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS N-Key Consumer Device node not found",
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// 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(());
|
||||
}
|
||||
Err(Box::new(RogError::NotSupported))
|
||||
}
|
||||
|
||||
/// Write an effect block
|
||||
#[inline]
|
||||
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), Box<dyn Error>> {
|
||||
if self.flip_effect_write {
|
||||
for row in effect.iter().rev() {
|
||||
self.write_bytes(row).await?;
|
||||
}
|
||||
} else {
|
||||
for row in effect.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
}
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Used to set a builtin mode and save the settings for it
|
||||
///
|
||||
/// 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>> {
|
||||
match mode {
|
||||
AuraModes::LedBrightness(n) => {
|
||||
let bytes: [u8; LED_MSG_LEN] = (&mode).into();
|
||||
self.write_bytes(&bytes).await?;
|
||||
config.read();
|
||||
config.kbd_led_brightness = n;
|
||||
config.write();
|
||||
info!("LED brightness set to {:#?}", n);
|
||||
}
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes).await?;
|
||||
} else {
|
||||
self.write_effect(&v).await?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
config.read();
|
||||
let mode_num: u8 = u8::from(&mode);
|
||||
self.write_mode(&mode).await?;
|
||||
config.kbd_backlight_mode = mode_num;
|
||||
config.set_mode_data(mode);
|
||||
config.write();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), Box<dyn Error>> {
|
||||
match mode {
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes).await?;
|
||||
} else {
|
||||
self.write_effect(v).await?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mode_num: u8 = u8::from(mode);
|
||||
match mode {
|
||||
AuraModes::MultiStatic(_) => {
|
||||
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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if self.supported_modes.contains(&mode_num) {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
self.write_bytes(&bytes).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.write_bytes(&LED_SET).await?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
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 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 log::LevelFilter;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.target(env_logger::Target::Stdout)
|
||||
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||
.filter(None, LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
info!("Version: {}", daemon::VERSION);
|
||||
start_daemon().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Timing is such that:
|
||||
// - interrupt write is minimum 1ms (sometimes lower)
|
||||
// - read interrupt must timeout, minimum of 1ms
|
||||
// - for a single usb packet, 2ms total.
|
||||
// - to maintain constant times of 1ms, per-key colours should use
|
||||
// the effect endpoint so that the complete colour block is written
|
||||
// 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>> {
|
||||
let laptop = match_laptop();
|
||||
let mut 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 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));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = fan_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), fan_mode_recv, None, None));
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
connection
|
||||
.send(
|
||||
charge_limit_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(config.bat_charge_limit),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -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 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RogError {
|
||||
ParseFanLevel,
|
||||
NotSupported,
|
||||
}
|
||||
|
||||
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::NotSupported => write!(f, "Not supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#![deny(unused_must_use)]
|
||||
/// Configuration loading, saving
|
||||
pub mod config;
|
||||
///
|
||||
pub mod ctrl_anime;
|
||||
///
|
||||
pub mod ctrl_charge;
|
||||
///
|
||||
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 config::Config;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc::Receiver, Mutex};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
pub static VERSION: &str = "1.0.3";
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
|
||||
#[async_trait]
|
||||
pub trait Controller {
|
||||
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<()>>;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
use asus_nb::{
|
||||
cli_options::{LedBrightness, SetAuraBuiltin},
|
||||
core_dbus::AuraDbusClient,
|
||||
};
|
||||
use daemon::ctrl_fan_cpu::FanLevel;
|
||||
use gumdrop::Options;
|
||||
use log::LevelFilter;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Options)]
|
||||
struct CLIStart {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "show program version number")]
|
||||
version: bool,
|
||||
#[options(meta = "VAL", help = "<off, low, med, high>")]
|
||||
kbd_bright: Option<LedBrightness>,
|
||||
#[options(meta = "PWR", help = "<silent, normal, boost>")]
|
||||
pwr_profile: Option<FanLevel>,
|
||||
#[options(meta = "CHRG", help = "<20-100>")]
|
||||
chg_limit: Option<u8>,
|
||||
#[options(command)]
|
||||
command: Option<Command>,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
enum Command {
|
||||
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||
LedMode(LedModeCommand),
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
struct LedModeCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(command, required)]
|
||||
command: Option<SetAuraBuiltin>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.target(env_logger::Target::Stdout)
|
||||
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||
.filter(None, LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let parsed = CLIStart::parse_args_default_or_exit();
|
||||
|
||||
if parsed.version {
|
||||
println!("Version: {}", daemon::VERSION);
|
||||
}
|
||||
|
||||
let writer = AuraDbusClient::new()?;
|
||||
|
||||
if let Some(Command::LedMode(mode)) = parsed.command {
|
||||
if let Some(command) = mode.command {
|
||||
writer.write_builtin_mode(&command.into())?
|
||||
}
|
||||
}
|
||||
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())?;
|
||||
}
|
||||
if let Some(chg_limit) = parsed.chg_limit {
|
||||
writer.write_charge_limit(chg_limit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
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};
|
||||
|
||||
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];
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Data structure should be nested array of [[u8; 33]; 56]
|
||||
pub struct AniMeDbusWriter {
|
||||
connection: Box<Connection>,
|
||||
block_time: u64,
|
||||
stop: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AniMeDbusWriter {
|
||||
#[inline]
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let connection = Connection::new_system()?;
|
||||
Ok(AniMeDbusWriter {
|
||||
connection: Box::new(connection),
|
||||
block_time: 25,
|
||||
stop: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// - 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);
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
use crate::error::AuraError;
|
||||
use gumdrop::Options;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct LedBrightness {
|
||||
level: u8,
|
||||
}
|
||||
impl LedBrightness {
|
||||
pub fn level(&self) -> u8 {
|
||||
self.level
|
||||
}
|
||||
}
|
||||
impl FromStr for LedBrightness {
|
||||
type Err = AuraError;
|
||||
|
||||
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 }),
|
||||
_ => {
|
||||
println!("Missing required argument, must be one of:\noff,low,med,high\n");
|
||||
Err(AuraError::ParseBrightness)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Colour(pub u8, pub u8, pub u8);
|
||||
impl Default for Colour {
|
||||
fn default() -> Self {
|
||||
Colour(255, 0, 0)
|
||||
}
|
||||
}
|
||||
impl FromStr for Colour {
|
||||
type Err = AuraError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.len() < 6 {
|
||||
return Err(AuraError::ParseColour);
|
||||
}
|
||||
let r = u8::from_str_radix(&s[0..2], 16).or(Err(AuraError::ParseColour))?;
|
||||
let g = u8::from_str_radix(&s[2..4], 16).or(Err(AuraError::ParseColour))?;
|
||||
let b = u8::from_str_radix(&s[4..6], 16).or(Err(AuraError::ParseColour))?;
|
||||
Ok(Colour(r, g, b))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum Speed {
|
||||
Low = 0xe1,
|
||||
Med = 0xeb,
|
||||
High = 0xf5,
|
||||
}
|
||||
impl Default for Speed {
|
||||
fn default() -> Self {
|
||||
Speed::Med
|
||||
}
|
||||
}
|
||||
impl FromStr for Speed {
|
||||
type Err = AuraError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_lowercase();
|
||||
match s.as_str() {
|
||||
"low" => Ok(Speed::Low),
|
||||
"med" => Ok(Speed::Med),
|
||||
"high" => Ok(Speed::High),
|
||||
_ => Err(AuraError::ParseSpeed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used for Rainbow mode.
|
||||
///
|
||||
/// Enum corresponds to the required integer value
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum Direction {
|
||||
Right,
|
||||
Left,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
impl Default for Direction {
|
||||
fn default() -> Self {
|
||||
Direction::Right
|
||||
}
|
||||
}
|
||||
impl FromStr for Direction {
|
||||
type Err = AuraError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.to_lowercase();
|
||||
match s.as_str() {
|
||||
"right" => Ok(Direction::Right),
|
||||
"up" => Ok(Direction::Up),
|
||||
"down" => Ok(Direction::Down),
|
||||
"left" => Ok(Direction::Left),
|
||||
_ => Err(AuraError::ParseDirection),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct TwoColourSpeed {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(no_long, meta = "HEX", help = "set the first RGB value e.g, ff00ff")]
|
||||
pub colour: Colour,
|
||||
#[options(no_long, meta = "HEX", help = "set the second RGB value e.g, ff00ff")]
|
||||
pub colour2: Colour,
|
||||
#[options(no_long, help = "set the speed: low, med, high")]
|
||||
pub speed: Speed,
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct SingleSpeed {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(no_long, meta = "WORD", help = "set the speed: low, med, high")]
|
||||
pub speed: Speed,
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct SingleColour {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(no_long, meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour: Colour,
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct MultiColour {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour1: Colour,
|
||||
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour2: Colour,
|
||||
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour3: Colour,
|
||||
#[options(meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour4: Colour,
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct SingleSpeedDirection {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(
|
||||
no_long,
|
||||
meta = "DIR",
|
||||
help = "set the direction: up, down, left, right"
|
||||
)]
|
||||
pub direction: Direction,
|
||||
#[options(no_long, help = "set the speed: low, med, high")]
|
||||
pub speed: Speed,
|
||||
}
|
||||
|
||||
#[derive(Default, Options, Deserialize, Serialize)]
|
||||
pub struct SingleColourSpeed {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(no_long, meta = "HEX", help = "set the RGB value e.g, ff00ff")]
|
||||
pub colour: Colour,
|
||||
#[options(no_long, help = "set the speed: low, med, high")]
|
||||
pub speed: Speed,
|
||||
}
|
||||
|
||||
/// Byte value for setting the built-in mode.
|
||||
///
|
||||
/// Enum corresponds to the required integer value
|
||||
#[derive(Options, Deserialize, Serialize)]
|
||||
pub enum SetAuraBuiltin {
|
||||
#[options(help = "set a single static colour")]
|
||||
Static(SingleColour),
|
||||
#[options(help = "pulse between one or two colours")]
|
||||
Breathe(TwoColourSpeed),
|
||||
#[options(help = "strobe through all colours")]
|
||||
Strobe(SingleSpeed),
|
||||
#[options(help = "rainbow cycling in one of four directions")]
|
||||
Rainbow(SingleSpeedDirection),
|
||||
#[options(help = "rain pattern mimicking raindrops")]
|
||||
Star(TwoColourSpeed),
|
||||
#[options(help = "rain pattern of three preset colours")]
|
||||
Rain(SingleSpeed),
|
||||
#[options(help = "pressed keys are highlighted to fade")]
|
||||
Highlight(SingleColourSpeed),
|
||||
#[options(help = "pressed keys generate horizontal laser")]
|
||||
Laser(SingleColourSpeed),
|
||||
#[options(help = "pressed keys ripple outwards like a splash")]
|
||||
Ripple(SingleColourSpeed),
|
||||
#[options(help = "set a rapid pulse")]
|
||||
Pulse(SingleColour),
|
||||
#[options(help = "set a vertical line zooming from left")]
|
||||
Comet(SingleColour),
|
||||
#[options(help = "set a wide vertical line zooming from left")]
|
||||
Flash(SingleColour),
|
||||
#[options(help = "4-zone multi-colour")]
|
||||
MultiStatic(MultiColour),
|
||||
}
|
||||
|
||||
impl Default for SetAuraBuiltin {
|
||||
fn default() -> Self {
|
||||
SetAuraBuiltin::Static(SingleColour {
|
||||
help: false,
|
||||
colour: Colour(255, 0, 0),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
use super::*;
|
||||
use crate::fancy::KeyColourArray;
|
||||
use dbus::channel::Sender;
|
||||
use dbus::{blocking::Connection, channel::Token, Message};
|
||||
use std::error::Error;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
/// Simplified way to write a effect block
|
||||
pub struct AuraDbusClient {
|
||||
connection: Box<Connection>,
|
||||
block_time: u64,
|
||||
stop: Arc<AtomicBool>,
|
||||
stop_token: Token,
|
||||
}
|
||||
|
||||
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| {
|
||||
if msg.read1::<&str>().is_ok() {
|
||||
stopper2.store(true, Ordering::Relaxed);
|
||||
}
|
||||
true
|
||||
})?;
|
||||
|
||||
Ok(AuraDbusClient {
|
||||
connection: Box::new(connection),
|
||||
block_time: 33333,
|
||||
stop,
|
||||
stop_token,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a single colour block.
|
||||
///
|
||||
/// Intentionally blocks for 10ms after sending to allow the block to
|
||||
/// be written to the keyboard EC. This should not be async.
|
||||
#[inline]
|
||||
pub fn write_colour_block(
|
||||
&mut self,
|
||||
key_colour_array: &KeyColourArray,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let group = key_colour_array.get();
|
||||
let mut vecs = Vec::with_capacity(group.len());
|
||||
for v in group {
|
||||
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();
|
||||
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)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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();
|
||||
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();
|
||||
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();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_builtin_mode(&self, mode: &AuraModes) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.write_keyboard_leds(mode)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
|
||||
self.write_keyboard_leds(&AuraModes::LedBrightness(level))?;
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
18
asus-notify/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "asus-notify"
|
||||
version = "3.0.0"
|
||||
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_json = "^1.0"
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
daemon = { path = "../daemon" }
|
||||
|
||||
[dependencies.notify-rust]
|
||||
version = "^4.0"
|
||||
default-features = false
|
||||
features = ["z"]
|
||||
110
asus-notify/src/main.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use daemon::config::Profile;
|
||||
use notify_rust::{Hint, Notification, NotificationHandle};
|
||||
use rog_dbus::{DbusProxies, Signals};
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("asus-notify version {}", env!("CARGO_PKG_VERSION"));
|
||||
println!(" daemon version {}", daemon::VERSION);
|
||||
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
||||
|
||||
// let mut cfg = Config::read_new()?;
|
||||
// let mut last_profile = String::new();
|
||||
|
||||
let (proxies, conn) = DbusProxies::new()?;
|
||||
let signals = Signals::new(&proxies)?;
|
||||
|
||||
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;
|
||||
|
||||
let recv = proxies.setup_recv(conn);
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
recv.next_signal().unwrap();
|
||||
|
||||
if let Ok(mut lock) = signals.gfx_vendor.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.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_chrg_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.profile.lock() {
|
||||
if let Some(profile) = lock.take() {
|
||||
if let Some(notif) = last_profile_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
if let Ok(profile) = serde_json::from_str(&profile) {
|
||||
let profile: Profile = profile;
|
||||
if let Ok(name) = proxies.profile().active_profile_name() {
|
||||
let x = do_thermal_notif(&profile, &name)?;
|
||||
last_profile_notif = Some(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.led_mode.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
20
asusctl/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "asusctl"
|
||||
version = "3.1.0"
|
||||
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_json = "^1.0"
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_types = { path = "../rog-types" }
|
||||
gumdrop = "^0.8"
|
||||
yansi-term = "^0.1"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
tinybmp = "^0.2.3"
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
@@ -1,9 +1,9 @@
|
||||
use asus_nb::anime_dbus::AniMeDbusWriter;
|
||||
use asus_nb::anime_matrix::{AniMeMatrix, AniMePacketType, HEIGHT, WIDTH};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::anime_matrix::{AniMeImageBuffer, AniMePacketType, HEIGHT, WIDTH};
|
||||
use tinybmp::{Bmp, Pixel};
|
||||
|
||||
fn main() {
|
||||
let mut writer = AniMeDbusWriter::new().unwrap();
|
||||
let (client, _) = AuraDbusClient::new().unwrap();
|
||||
|
||||
let bmp =
|
||||
Bmp::from_slice(include_bytes!("non-skewed_r.bmp")).expect("Failed to parse BMP image");
|
||||
@@ -11,12 +11,13 @@ fn main() {
|
||||
//assert_eq!(pixels.len(), 56 * 56);
|
||||
|
||||
// Try an outline, top and right
|
||||
let mut matrix = AniMeMatrix::new();
|
||||
let mut matrix = AniMeImageBuffer::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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,5 +38,5 @@ fn main() {
|
||||
// println!("{:?}", matrix[0].to_vec());
|
||||
// println!("{:?}", matrix[1].to_vec());
|
||||
|
||||
writer.write_image(&mut matrix).unwrap();
|
||||
//client.proxies().anime().set_brightness(&mut matrix).unwrap();
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
use asus_nb::{
|
||||
core_dbus::AuraDbusClient,
|
||||
fancy::{GX502Layout, Key, KeyColourArray, KeyLayout},
|
||||
};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::fancy::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
||||
use std::collections::LinkedList;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -54,7 +52,7 @@ impl Ball {
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut writer = AuraDbusClient::new()?;
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
let mut colours = KeyColourArray::new();
|
||||
|
||||
@@ -62,7 +60,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let mut balls = [Ball::new(2, 1, 12), Ball::new(4, 6, 12)];
|
||||
|
||||
writer.init_effect()?;
|
||||
dbus.proxies().led().init_effect()?;
|
||||
|
||||
let rows = layout.get_rows();
|
||||
loop {
|
||||
@@ -89,10 +87,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
*c.2 = 255;
|
||||
};
|
||||
}
|
||||
dbus.proxies().led().set_per_key(&colours)?;
|
||||
|
||||
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(10));
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
use asus_nb::{
|
||||
core_dbus::AuraDbusClient,
|
||||
fancy::{GX502Layout, KeyColourArray, KeyLayout},
|
||||
};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::fancy::{GX502Layout, KeyColourArray, KeyLayout};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut writer = AuraDbusClient::new()?;
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
let layout = GX502Layout::default();
|
||||
|
||||
writer.init_effect()?;
|
||||
dbus.proxies().led().init_effect()?;
|
||||
let rows = layout.get_rows();
|
||||
|
||||
let mut column = 0;
|
||||
@@ -25,7 +23,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
column += 1;
|
||||
}
|
||||
|
||||
writer.write_colour_block(&key_colours)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(30));
|
||||
dbus.proxies().led().set_per_key(&key_colours)?;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
use asus_nb::{
|
||||
core_dbus::AuraDbusClient,
|
||||
fancy::{GX502Layout, Key, KeyColourArray, KeyLayout},
|
||||
};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::fancy::{GX502Layout, Key, KeyColourArray, KeyLayout};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut writer = AuraDbusClient::new()?;
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
let mut key_colours = KeyColourArray::new();
|
||||
let layout = GX502Layout::default();
|
||||
|
||||
writer.init_effect()?;
|
||||
dbus.proxies().led().init_effect()?;
|
||||
let rows = layout.get_rows();
|
||||
loop {
|
||||
for (r, row) in rows.iter().enumerate() {
|
||||
@@ -48,7 +46,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
*key_colours.key(Key::S).unwrap().0 = 255;
|
||||
*key_colours.key(Key::D).unwrap().0 = 255;
|
||||
|
||||
writer.write_colour_block(&key_colours)?;
|
||||
dbus.proxies().led().set_per_key(&key_colours)?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,14 +1,12 @@
|
||||
use asus_nb::{
|
||||
core_dbus::AuraDbusClient,
|
||||
fancy::{Key, KeyColourArray},
|
||||
};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::fancy::{Key, KeyColourArray};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut writer = AuraDbusClient::new()?;
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
let mut key_colours = KeyColourArray::new();
|
||||
|
||||
writer.init_effect()?;
|
||||
dbus.proxies().led().init_effect()?;
|
||||
loop {
|
||||
let count = 49;
|
||||
for _ in 0..count {
|
||||
@@ -18,7 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
*key_colours.key(Key::N).unwrap().0 += 5;
|
||||
*key_colours.key(Key::U).unwrap().0 += 5;
|
||||
*key_colours.key(Key::X).unwrap().0 += 5;
|
||||
writer.write_colour_block(&key_colours)?;
|
||||
dbus.proxies().led().set_per_key(&key_colours)?;
|
||||
}
|
||||
for _ in 0..count {
|
||||
*key_colours.key(Key::ROG).unwrap().0 -= 5;
|
||||
@@ -27,7 +25,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
*key_colours.key(Key::N).unwrap().0 -= 5;
|
||||
*key_colours.key(Key::U).unwrap().0 -= 5;
|
||||
*key_colours.key(Key::X).unwrap().0 -= 5;
|
||||
writer.write_colour_block(&key_colours)?;
|
||||
dbus.proxies().led().set_per_key(&key_colours)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
use asus_nb::{
|
||||
core_dbus::AuraDbusClient,
|
||||
fancy::{GX502Layout, KeyColourArray, KeyLayout},
|
||||
};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::fancy::{GX502Layout, KeyColourArray, KeyLayout};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut writer = AuraDbusClient::new()?;
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
let mut key_colours = KeyColourArray::new();
|
||||
let layout = GX502Layout::default();
|
||||
|
||||
writer.init_effect()?;
|
||||
dbus.proxies().led().init_effect()?;
|
||||
let rows = layout.get_rows();
|
||||
|
||||
let mut fade = 50;
|
||||
@@ -23,7 +21,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
writer.write_colour_block(&key_colours)?;
|
||||
dbus.proxies().led().set_per_key(&key_colours)?;
|
||||
|
||||
if flip {
|
||||
if fade > 1 {
|
||||
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
333
asusctl/src/main.rs
Normal file
@@ -0,0 +1,333 @@
|
||||
use gumdrop::{Opt, Options};
|
||||
use rog_dbus::AuraDbusClient;
|
||||
use rog_types::{
|
||||
anime_matrix::{AniMeDataBuffer, FULL_PANE_LEN},
|
||||
cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin},
|
||||
gfx_vendors::GfxVendors,
|
||||
profile::{FanLevel, ProfileCommand, ProfileEvent},
|
||||
};
|
||||
use std::env::args;
|
||||
use yansi_term::Colour::Green;
|
||||
use yansi_term::Colour::Red;
|
||||
|
||||
#[derive(Default, Options)]
|
||||
struct CLIStart {
|
||||
#[options(help_flag, help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "show program version number")]
|
||||
version: bool,
|
||||
#[options(help = "show supported functions of this laptop")]
|
||||
show_supported: bool,
|
||||
#[options(meta = "", help = "<off, low, med, high>")]
|
||||
kbd_bright: Option<LedBrightness>,
|
||||
#[options(
|
||||
meta = "",
|
||||
help = "<silent, normal, boost>, set fan mode independent of profile"
|
||||
)]
|
||||
fan_mode: Option<FanLevel>,
|
||||
#[options(meta = "", help = "<20-100>")]
|
||||
chg_limit: Option<u8>,
|
||||
#[options(command)]
|
||||
command: Option<CliCommand>,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
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),
|
||||
#[options(help = "Change bios settings")]
|
||||
Bios(BiosCommand),
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
struct LedModeCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
struct GraphicsCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(
|
||||
meta = "",
|
||||
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(
|
||||
meta = "",
|
||||
help = "turn on/off the panel (accept/reject write requests)"
|
||||
)]
|
||||
turn: Option<AniMeStatusValue>,
|
||||
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
|
||||
boot: Option<AniMeStatusValue>,
|
||||
#[options(command)]
|
||||
command: Option<AniMeActions>,
|
||||
}
|
||||
|
||||
#[derive(Options, Debug)]
|
||||
struct BiosCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(meta = "", no_long, help = "toggle bios POST sound")]
|
||||
post_sound_set: Option<bool>,
|
||||
#[options(no_long, help = "read bios POST sound")]
|
||||
post_sound_get: bool,
|
||||
#[options(meta = "", no_long, help = "toggle GPU to/from dedicated mode")]
|
||||
dedicated_gfx_set: Option<bool>,
|
||||
#[options(no_long, help = "get GPU mode")]
|
||||
dedicated_gfx_get: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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!(" asusctl version {}", env!("CARGO_PKG_VERSION"));
|
||||
println!(" rog-dbus version {}", rog_dbus::VERSION);
|
||||
println!("rog-types version {}", rog_types::VERSION);
|
||||
}
|
||||
|
||||
let (dbus, _) = AuraDbusClient::new()?;
|
||||
|
||||
match parsed.command {
|
||||
Some(CliCommand::LedMode(mode)) => {
|
||||
if (mode.command.is_none() && !mode.prev_mode && !mode.next_mode) || mode.help {
|
||||
println!("Missing arg or command\n\n{}", mode.self_usage());
|
||||
if let Some(lst) = mode.self_command_list() {
|
||||
println!("\n{}", lst);
|
||||
}
|
||||
println!("\nHelp can also be requested on modes, e.g: static --help");
|
||||
}
|
||||
if mode.next_mode && mode.prev_mode {
|
||||
println!("Please specify either next or previous")
|
||||
}
|
||||
if mode.next_mode {
|
||||
dbus.proxies().led().next_led_mode()?;
|
||||
} else if mode.prev_mode {
|
||||
dbus.proxies().led().prev_led_mode()?;
|
||||
} else if let Some(command) = mode.command {
|
||||
dbus.proxies().led().set_led_mode(&command.into())?
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Profile(cmd)) => {
|
||||
if (!cmd.next
|
||||
&& !cmd.create
|
||||
&& cmd.curve.is_none()
|
||||
&& cmd.max_percentage.is_none()
|
||||
&& cmd.min_percentage.is_none()
|
||||
&& cmd.preset.is_none()
|
||||
&& cmd.profile.is_none()
|
||||
&& cmd.turbo.is_none())
|
||||
|| cmd.help
|
||||
{
|
||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||
if let Some(lst) = cmd.self_command_list() {
|
||||
println!("\n{}", lst);
|
||||
}
|
||||
}
|
||||
if cmd.next {
|
||||
dbus.proxies().profile().next_fan()?;
|
||||
} else {
|
||||
dbus.proxies()
|
||||
.profile()
|
||||
.write_command(&ProfileEvent::Cli(cmd))?
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Graphics(cmd)) => do_gfx(cmd, &dbus)?,
|
||||
Some(CliCommand::AniMe(cmd)) => {
|
||||
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
|
||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||
if let Some(lst) = cmd.self_command_list() {
|
||||
println!("\n{}", lst);
|
||||
}
|
||||
}
|
||||
if let Some(anime_turn) = cmd.turn {
|
||||
dbus.proxies().anime().toggle_on(anime_turn.into())?
|
||||
}
|
||||
if let Some(anime_boot) = cmd.boot {
|
||||
dbus.proxies().anime().toggle_boot_on(anime_boot.into())?
|
||||
}
|
||||
if let Some(action) = cmd.command {
|
||||
match action {
|
||||
AniMeActions::Leds(anime_leds) => {
|
||||
let mut data = AniMeDataBuffer::new();
|
||||
data.set([anime_leds.led_brightness(); FULL_PANE_LEN]);
|
||||
dbus.proxies().anime().write_direct(data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Bios(cmd)) => {
|
||||
if (cmd.dedicated_gfx_set.is_none()
|
||||
&& !cmd.dedicated_gfx_get
|
||||
&& cmd.post_sound_set.is_none()
|
||||
&& !cmd.post_sound_get)
|
||||
|| cmd.help
|
||||
{
|
||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||
if let Some(lst) = cmd.self_command_list() {
|
||||
println!("\n{}", lst);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(opt) = cmd.post_sound_set {
|
||||
dbus.proxies().rog_bios().set_post_sound(opt)?;
|
||||
}
|
||||
if cmd.post_sound_get {
|
||||
let res = if dbus.proxies().rog_bios().get_post_sound()? == 1 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
println!("Bios POST sound on: {}", res);
|
||||
}
|
||||
if let Some(opt) = cmd.dedicated_gfx_set {
|
||||
dbus.proxies().rog_bios().set_dedicated_gfx(opt)?;
|
||||
}
|
||||
if cmd.dedicated_gfx_get {
|
||||
let res = if dbus.proxies().rog_bios().get_dedicated_gfx()? == 1 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
println!("Bios dedicated GPU on: {}", res);
|
||||
println!("You must reboot your system to activate dedicated Nvidia mode");
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if (!parsed.show_supported
|
||||
&& parsed.kbd_bright.is_none()
|
||||
&& parsed.fan_mode.is_none()
|
||||
&& parsed.chg_limit.is_none())
|
||||
|| parsed.help
|
||||
{
|
||||
println!("{}", CLIStart::usage());
|
||||
println!();
|
||||
println!("{}", CLIStart::command_list().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(brightness) = parsed.kbd_bright {
|
||||
match brightness.level() {
|
||||
None => {
|
||||
let level = dbus.proxies().led().get_led_brightness()?;
|
||||
println!("Current keyboard led brightness: {}", level.to_string());
|
||||
}
|
||||
Some(level) => dbus.proxies().led().set_brightness(level)?,
|
||||
}
|
||||
}
|
||||
|
||||
if parsed.show_supported {
|
||||
let dat = dbus.proxies().supported().get_supported_functions()?;
|
||||
println!("Supported laptop functions:\n{}", dat.to_string());
|
||||
}
|
||||
|
||||
if let Some(fan_level) = parsed.fan_mode {
|
||||
dbus.proxies().profile().write_fan_mode(fan_level.into())?;
|
||||
}
|
||||
if let Some(chg_limit) = parsed.chg_limit {
|
||||
dbus.proxies().charge().write_limit(chg_limit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_gfx(
|
||||
command: GraphicsCommand,
|
||||
dbus_client: &AuraDbusClient,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mode) = command.mode {
|
||||
println!("Changing graphics modes...");
|
||||
println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`");
|
||||
|
||||
if do_gfx_action(
|
||||
command.force,
|
||||
"This will restart your display-manager. Please save all work!",
|
||||
"Setting graphics mode...",
|
||||
) {
|
||||
dbus_client.proxies().gfx().gfx_write_mode(mode.into())?;
|
||||
let res = dbus_client.gfx_wait_changed()?;
|
||||
println!("{}", res);
|
||||
std::process::exit(1)
|
||||
}
|
||||
std::process::exit(-1)
|
||||
}
|
||||
if command.get {
|
||||
let res = dbus_client.proxies().gfx().gfx_get_mode()?;
|
||||
println!("Current graphics mode: {}", res);
|
||||
}
|
||||
if command.pow {
|
||||
let res = dbus_client.proxies().gfx().gfx_get_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, ask_msg: &str, ok_msg: &str) -> bool {
|
||||
println!("{}", Red.paint(&format!("{} Continue?", ask_msg)));
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
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 {
|
||||
println!("{}", Green.paint(&format!("{}", ok_msg)));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "asus-nb-ctrl"
|
||||
version = "1.0.3"
|
||||
name = "daemon"
|
||||
version = "3.1.2"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -13,37 +13,31 @@ edition = "2018"
|
||||
name = "daemon"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "asusctl"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "asusd"
|
||||
path = "src/daemon.rs"
|
||||
|
||||
[dependencies]
|
||||
asus-nb = { path = "../asus-nb" }
|
||||
rusb = "^0.6.0"
|
||||
udev = "^0.4.0"
|
||||
async-trait = "0.1.36"
|
||||
rog_types = { path = "../rog-types" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rusb = "^0.7"
|
||||
udev = "^0.6"
|
||||
|
||||
# cli and logging
|
||||
gumdrop = "^0.8.0"
|
||||
log = "^0.4.8"
|
||||
env_logger = "^0.7.1"
|
||||
log = "^0.4"
|
||||
env_logger = "^0.8"
|
||||
|
||||
# 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"
|
||||
zvariant = "^2.4"
|
||||
|
||||
# serialisation
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
toml = "0.4.6"
|
||||
toml = "^0.5"
|
||||
|
||||
# Device control
|
||||
sysfs-class = "^0.1.2" # used for backlight control and baord ID
|
||||
rog_fan_curve = { version = "0.1", features = ["serde"] }
|
||||
# cpu power management
|
||||
intel-pstate = "^0.2.1"
|
||||
intel-pstate = "^0.2"
|
||||
204
daemon/src/config.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use log::{error, info, warn};
|
||||
use rog_fan_curve::Curve;
|
||||
use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::config_old::*;
|
||||
use crate::VERSION;
|
||||
|
||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub gfx_mode: GfxVendors,
|
||||
pub gfx_managed: bool,
|
||||
pub active_profile: String,
|
||||
pub toggle_profiles: Vec<String>,
|
||||
#[serde(skip)]
|
||||
pub curr_fan_mode: 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: BTreeMap<String, Profile>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let mut pwr = BTreeMap::new();
|
||||
pwr.insert("normal".into(), Profile::new(0, 100, true, 0, None));
|
||||
pwr.insert("boost".into(), Profile::new(0, 100, true, 1, None));
|
||||
pwr.insert("silent".into(), Profile::new(0, 100, true, 2, None));
|
||||
|
||||
Config {
|
||||
gfx_mode: GfxVendors::Hybrid,
|
||||
gfx_managed: true,
|
||||
active_profile: "normal".into(),
|
||||
toggle_profiles: vec!["normal".into(), "boost".into(), "silent".into()],
|
||||
curr_fan_mode: 0,
|
||||
bat_charge_limit: 100,
|
||||
kbd_led_brightness: 1,
|
||||
kbd_backlight_mode: 0,
|
||||
kbd_backlight_modes: Vec::new(),
|
||||
power_profiles: pwr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||
pub fn load(supported_led_modes: &[u8]) -> Self {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&CONFIG_PATH)
|
||||
.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(read_len) = file.read_to_string(&mut buf) {
|
||||
if read_len == 0 {
|
||||
return Config::create_default(&mut file, &supported_led_modes);
|
||||
} else {
|
||||
if let Ok(data) = serde_json::from_str(&buf) {
|
||||
return data;
|
||||
} else if let Ok(data) = serde_json::from_str::<ConfigV301>(&buf) {
|
||||
let config = data.into_current();
|
||||
config.write();
|
||||
info!("Updated config version to: {}", VERSION);
|
||||
return config;
|
||||
} else if let Ok(data) = serde_json::from_str::<ConfigV222>(&buf) {
|
||||
let config = data.into_current();
|
||||
config.write();
|
||||
info!("Updated config version to: {}", VERSION);
|
||||
return config;
|
||||
} else if let Ok(data) = serde_json::from_str::<ConfigV212>(&buf) {
|
||||
let config = data.into_current();
|
||||
config.write();
|
||||
info!("Updated config version to: {}", VERSION);
|
||||
return config;
|
||||
}
|
||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
||||
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
||||
}
|
||||
}
|
||||
Config::create_default(&mut file, &supported_led_modes)
|
||||
}
|
||||
|
||||
fn create_default(file: &mut File, supported_led_modes: &[u8]) -> Self {
|
||||
// create a default config here
|
||||
let mut config = Config::default();
|
||||
|
||||
for n in supported_led_modes {
|
||||
config.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
}
|
||||
|
||||
// Should be okay to unwrap this as is since it is a Default
|
||||
let json = serde_json::to_string_pretty(&config).unwrap();
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
||||
config
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
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();
|
||||
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||
if l == 0 {
|
||||
warn!("File is empty {}", CONFIG_PATH);
|
||||
} else {
|
||||
let x: Config = serde_json::from_str(&buf)
|
||||
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
|
||||
*self = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||
}
|
||||
|
||||
pub fn set_mode_data(&mut self, mode: AuraModes) {
|
||||
let byte: u8 = (&mode).into();
|
||||
for (index, n) in self.kbd_backlight_modes.iter().enumerate() {
|
||||
if byte == u8::from(n) {
|
||||
// Consume it, OMNOMNOMNOM
|
||||
self.kbd_backlight_modes[index] = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_led_mode_data(&self, num: u8) -> Option<&AuraModes> {
|
||||
for mode in &self.kbd_backlight_modes {
|
||||
if u8::from(mode) == num {
|
||||
return Some(mode);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Profile {
|
||||
pub min_percentage: u8,
|
||||
pub max_percentage: u8,
|
||||
pub turbo: bool,
|
||||
pub fan_preset: u8,
|
||||
pub fan_curve: Option<Curve>,
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub type CPUSettings = Profile;
|
||||
|
||||
impl Default for Profile {
|
||||
fn default() -> Self {
|
||||
Profile {
|
||||
min_percentage: 0,
|
||||
max_percentage: 100,
|
||||
turbo: false,
|
||||
fan_preset: 0,
|
||||
fan_curve: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
pub fn new(
|
||||
min_percentage: u8,
|
||||
max_percentage: u8,
|
||||
turbo: bool,
|
||||
fan_preset: u8,
|
||||
fan_curve: Option<Curve>,
|
||||
) -> Self {
|
||||
Profile {
|
||||
min_percentage,
|
||||
max_percentage,
|
||||
turbo,
|
||||
fan_preset,
|
||||
fan_curve,
|
||||
}
|
||||
}
|
||||
}
|
||||
100
daemon/src/config_old.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use rog_types::{aura_modes::AuraModes, gfx_vendors::GfxVendors};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::config::{Config, Profile};
|
||||
|
||||
/// for parsing old v2.1.2 config
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct ConfigV212 {
|
||||
gfx_managed: bool,
|
||||
bat_charge_limit: u8,
|
||||
active_profile: String,
|
||||
toggle_profiles: Vec<String>,
|
||||
power_profiles: BTreeMap<String, Profile>,
|
||||
power_profile: u8,
|
||||
kbd_led_brightness: u8,
|
||||
kbd_backlight_mode: u8,
|
||||
kbd_backlight_modes: Vec<AuraModes>,
|
||||
}
|
||||
|
||||
impl ConfigV212 {
|
||||
pub(crate) fn into_current(self) -> Config {
|
||||
Config {
|
||||
gfx_mode: GfxVendors::Hybrid,
|
||||
gfx_managed: self.gfx_managed,
|
||||
active_profile: self.active_profile,
|
||||
toggle_profiles: self.toggle_profiles,
|
||||
curr_fan_mode: self.power_profile,
|
||||
bat_charge_limit: self.bat_charge_limit,
|
||||
kbd_led_brightness: self.kbd_led_brightness,
|
||||
kbd_backlight_mode: self.kbd_backlight_mode,
|
||||
kbd_backlight_modes: self.kbd_backlight_modes,
|
||||
power_profiles: self.power_profiles,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// for parsing old v2.2.2 config
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct ConfigV222 {
|
||||
gfx_managed: bool,
|
||||
bat_charge_limit: u8,
|
||||
active_profile: String,
|
||||
toggle_profiles: Vec<String>,
|
||||
power_profiles: BTreeMap<String, Profile>,
|
||||
power_profile: u8,
|
||||
kbd_led_brightness: u8,
|
||||
kbd_backlight_mode: u8,
|
||||
kbd_backlight_modes: Vec<AuraModes>,
|
||||
}
|
||||
|
||||
impl ConfigV222 {
|
||||
pub(crate) fn into_current(self) -> Config {
|
||||
Config {
|
||||
gfx_mode: GfxVendors::Hybrid,
|
||||
gfx_managed: self.gfx_managed,
|
||||
active_profile: self.active_profile,
|
||||
toggle_profiles: self.toggle_profiles,
|
||||
curr_fan_mode: self.power_profile,
|
||||
bat_charge_limit: self.bat_charge_limit,
|
||||
kbd_led_brightness: self.kbd_led_brightness,
|
||||
kbd_backlight_mode: self.kbd_backlight_mode,
|
||||
kbd_backlight_modes: self.kbd_backlight_modes,
|
||||
power_profiles: self.power_profiles,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub(crate) struct ConfigV301 {
|
||||
pub gfx_managed: bool,
|
||||
pub gfx_nv_mode_is_dedicated: bool,
|
||||
pub active_profile: String,
|
||||
pub toggle_profiles: Vec<String>,
|
||||
// TODO: remove power_profile
|
||||
#[serde(skip)]
|
||||
pub curr_fan_mode: 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: BTreeMap<String, Profile>,
|
||||
}
|
||||
|
||||
impl ConfigV301 {
|
||||
pub(crate) fn into_current(self) -> Config {
|
||||
Config {
|
||||
gfx_mode: GfxVendors::Hybrid,
|
||||
gfx_managed: self.gfx_managed,
|
||||
active_profile: self.active_profile,
|
||||
toggle_profiles: self.toggle_profiles,
|
||||
curr_fan_mode: self.curr_fan_mode,
|
||||
bat_charge_limit: self.bat_charge_limit,
|
||||
kbd_led_brightness: self.kbd_led_brightness,
|
||||
kbd_backlight_mode: self.kbd_backlight_mode,
|
||||
kbd_backlight_modes: self.kbd_backlight_modes,
|
||||
power_profiles: self.power_profiles,
|
||||
}
|
||||
}
|
||||
}
|
||||
265
daemon/src/ctrl_anime.rs
Normal file
@@ -0,0 +1,265 @@
|
||||
const INIT_STR: &str = "ASUS Tech.Inc.";
|
||||
const PACKET_SIZE: usize = 640;
|
||||
|
||||
// Only these two packets must be 17 bytes
|
||||
const DEV_PAGE: u8 = 0x5e;
|
||||
// These bytes are in [1] position of the array
|
||||
const WRITE: u8 = 0xc0;
|
||||
const INIT: u8 = 0xc2;
|
||||
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 log::{error, info, warn};
|
||||
use rog_types::{
|
||||
anime_matrix::{
|
||||
AniMeDataBuffer, AniMeImageBuffer, AniMePacketType, ANIME_PANE1_PREFIX, ANIME_PANE2_PREFIX,
|
||||
},
|
||||
error::AuraError,
|
||||
};
|
||||
use rusb::{Device, DeviceHandle};
|
||||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::GetSupported;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AnimeSupportedFunctions(bool);
|
||||
|
||||
impl GetSupported for CtrlAnimeDisplay {
|
||||
type A = AnimeSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(0x0b05, 0x193b).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlAnimeDisplay {
|
||||
handle: DeviceHandle<rusb::GlobalContext>,
|
||||
}
|
||||
|
||||
//AnimatrixWrite
|
||||
pub trait Dbus {
|
||||
/// Write an image 34x56 pixels. Each pixel is 0-255 greyscale.
|
||||
fn write_image(&self, input: AniMeImageBuffer);
|
||||
|
||||
/// Write a direct stream of data
|
||||
fn write_direct(&self, input: AniMeDataBuffer);
|
||||
|
||||
fn set_on_off(&self, status: bool);
|
||||
|
||||
fn set_boot_on_off(&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 {
|
||||
/// Writes a 34x56 image
|
||||
fn write_image(&self, input: AniMeImageBuffer) {
|
||||
self.write_image_buffer(input)
|
||||
.map_or_else(|err| warn!("{}", err), |()| info!("Writing image to Anime"));
|
||||
}
|
||||
|
||||
/// Writes a data stream of length
|
||||
fn write_direct(&self, input: AniMeDataBuffer) {
|
||||
self.write_data_buffer(input)
|
||||
.map_or_else(|err| warn!("{}", err), |()| info!("Writing data to Anime"));
|
||||
}
|
||||
|
||||
fn set_on_off(&self, status: bool) {
|
||||
let mut buffer = [0u8; PACKET_SIZE];
|
||||
buffer[0] = DEV_PAGE;
|
||||
buffer[1] = WRITE;
|
||||
buffer[2] = ON_OFF;
|
||||
|
||||
if status {
|
||||
buffer[3] = 0x03;
|
||||
} else {
|
||||
buffer[3] = 0x00;
|
||||
}
|
||||
|
||||
self.write_bytes(&buffer);
|
||||
}
|
||||
|
||||
fn set_boot_on_off(&self, status: bool) {
|
||||
let status_str = if status { "on" } else { "off" };
|
||||
|
||||
self.do_set_boot(status).map_or_else(
|
||||
|err| warn!("{}", err),
|
||||
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
|
||||
);
|
||||
self.do_apply().map_or_else(
|
||||
|err| warn!("{}", err),
|
||||
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
let mut device = device.open()?;
|
||||
device.reset()?;
|
||||
|
||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||
error!("Auto-detach kernel driver failed: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
device.claim_interface(0).map_err(|err| {
|
||||
error!("Could not claim device interface: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
info!("Device has an AniMe Matrix display");
|
||||
let ctrl = CtrlAnimeDisplay { handle: device };
|
||||
ctrl.do_initialization()?;
|
||||
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
let device_desc = device.device_descriptor()?;
|
||||
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
||||
return Ok(device);
|
||||
}
|
||||
}
|
||||
Err(rusb::Error::NoDevice)
|
||||
}
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
#[inline]
|
||||
fn write_bytes(&self, message: &[u8]) {
|
||||
match self.handle.write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
0x35e, // value
|
||||
0x00, // index
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(err) => match err {
|
||||
rusb::Error::Timeout => {}
|
||||
_ => error!("Failed to write to led interrupt: {}", err),
|
||||
},
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn write_data_buffer(&self, buffer: AniMeDataBuffer) -> Result<(), AuraError> {
|
||||
let mut image = AniMePacketType::from(buffer);
|
||||
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
|
||||
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
|
||||
|
||||
for row in image.iter() {
|
||||
self.write_bytes(row);
|
||||
}
|
||||
self.do_flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write an Animatrix image
|
||||
///
|
||||
/// The expected USB 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
|
||||
///
|
||||
/// # Note:
|
||||
/// The vectors are expected to contain the full sequence of bytes as follows
|
||||
///
|
||||
/// - Write pane 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
|
||||
/// - Write pane 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
|
||||
///
|
||||
/// Where led brightness is 0..255, low to high
|
||||
#[inline]
|
||||
fn write_image_buffer(&self, buffer: AniMeImageBuffer) -> Result<(), AuraError> {
|
||||
let mut image = AniMePacketType::from(buffer);
|
||||
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
|
||||
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
|
||||
|
||||
for row in image.iter() {
|
||||
self.write_bytes(row);
|
||||
}
|
||||
self.do_flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_initialization(&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);
|
||||
|
||||
// clear the init array and write other init message
|
||||
for ch in init.iter_mut() {
|
||||
*ch = 0;
|
||||
}
|
||||
init[0] = DEV_PAGE; // write it to be sure?
|
||||
init[1] = INIT;
|
||||
|
||||
self.write_bytes(&init);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_flush(&self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = WRITE;
|
||||
flush[2] = 0x03;
|
||||
|
||||
self.write_bytes(&flush);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_set_boot(&self, status: bool) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = SET;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = if status { 0x00 } else { 0x80 };
|
||||
|
||||
self.write_bytes(&flush);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_apply(&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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
125
daemon/src/ctrl_charge.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
//use crate::dbus::DbusEvents;
|
||||
use log::{info, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ChargeSupportedFunctions {
|
||||
pub charge_level_set: bool,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlCharge {
|
||||
type A = ChargeSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
ChargeSupportedFunctions {
|
||||
charge_level_set: CtrlCharge::get_battery_path().is_ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlCharge {
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
self.set(config.bat_charge_limit, &mut config)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlCharge {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
CtrlCharge::get_battery_path()?;
|
||||
Ok(CtrlCharge { config })
|
||||
}
|
||||
|
||||
fn get_battery_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(BAT_CHARGE_PATH).exists() {
|
||||
Ok(BAT_CHARGE_PATH)
|
||||
} else {
|
||||
Err(RogError::MissingFunction(
|
||||
"Charge control not available, you may require a v5.8.10 series kernel or newer"
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
if !(20..=100).contains(&limit) {
|
||||
warn!(
|
||||
"Unable to set battery charge limit, must be between 20-100: requested {}",
|
||||
limit
|
||||
);
|
||||
}
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(BAT_CHARGE_PATH)
|
||||
.map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?;
|
||||
file.write_all(limit.to_string().as_bytes())
|
||||
.map_err(|err| RogError::Write(BAT_CHARGE_PATH.into(), err))?;
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
config.read();
|
||||
config.bat_charge_limit = limit;
|
||||
config.write();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
347
daemon/src/ctrl_fan_cpu.rs
Normal file
@@ -0,0 +1,347 @@
|
||||
use crate::error::RogError;
|
||||
use crate::{
|
||||
config::{Config, Profile},
|
||||
GetSupported,
|
||||
};
|
||||
use log::{info, warn};
|
||||
use rog_types::profile::{FanLevel, ProfileEvent};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
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 {
|
||||
pub path: &'static str,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FanCpuSupportedFunctions {
|
||||
pub stock_fan_modes: bool,
|
||||
pub min_max_freq: bool,
|
||||
pub fan_curve_set: bool,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlFanAndCPU {
|
||||
type A = FanCpuSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
FanCpuSupportedFunctions {
|
||||
stock_fan_modes: CtrlFanAndCPU::get_fan_path().is_ok(),
|
||||
min_max_freq: intel_pstate::PState::new().is_ok(),
|
||||
fan_curve_set: rog_fan_curve::Board::from_board_name().is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DbusFanAndCpu {
|
||||
inner: Arc<Mutex<CtrlFanAndCPU>>,
|
||||
}
|
||||
|
||||
impl DbusFanAndCpu {
|
||||
pub fn new(inner: Arc<Mutex<CtrlFanAndCPU>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: add_to_server {}", 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 profile = config.active_profile.clone();
|
||||
self.set(&profile, &mut config)?;
|
||||
// info!(
|
||||
// "Reloaded fan mode: {:?}",
|
||||
// FanLevel::from(config.power_profile)
|
||||
// );
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlFanAndCPU {
|
||||
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, config })
|
||||
}
|
||||
|
||||
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(RogError::MissingFunction(
|
||||
"Fan mode not available, you may require a v5.8.10 series kernel or newer".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle to next profile in list
|
||||
pub(super) fn do_next_profile(&mut self, config: &mut Config) -> Result<(), RogError> {
|
||||
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, config)?;
|
||||
|
||||
info!("Profile was changed: {}", &new_profile);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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.curr_fan_mode = 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));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_profile_event(
|
||||
&mut self,
|
||||
event: &ProfileEvent,
|
||||
config: &mut Config,
|
||||
) -> Result<(), RogError> {
|
||||
match event {
|
||||
ProfileEvent::Toggle => self.do_next_profile(config)?,
|
||||
ProfileEvent::ChangeMode(mode) => {
|
||||
self.set_fan_mode(*mode, config)?;
|
||||
let mode = config.active_profile.clone();
|
||||
self.set_pstate_for_fan_mode(&mode, config)?;
|
||||
self.set_fan_curve_for_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))?;
|
||||
config.curr_fan_mode = mode_config.fan_preset;
|
||||
fan_ctrl
|
||||
.write_all(format!("{}\n", mode_config.fan_preset).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
|
||||
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() {
|
||||
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| 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(())
|
||||
}
|
||||
}
|
||||
30
daemon/src/ctrl_gfx/error.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
use crate::error::RogError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GfxError {
|
||||
ParseVendor,
|
||||
Bus(String, std::io::Error),
|
||||
DisplayManager(String),
|
||||
}
|
||||
|
||||
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::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
|
||||
GfxError::DisplayManager(detail) => write!(f, "Display manager: {}", detail),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for GfxError {}
|
||||
|
||||
impl From<GfxError> for RogError {
|
||||
fn from(err: GfxError) -> Self {
|
||||
RogError::GfxSwitching(err)
|
||||
}
|
||||
}
|
||||
408
daemon/src/ctrl_gfx/gfx.rs
Normal file
@@ -0,0 +1,408 @@
|
||||
use ctrl_gfx::error::GfxError;
|
||||
use ctrl_gfx::*;
|
||||
use log::{error, info, warn};
|
||||
use rog_types::gfx_vendors::GfxVendors;
|
||||
use std::iter::FromIterator;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use std::{io::Write, ops::Add, path::Path};
|
||||
use std::{sync::Arc, sync::Mutex};
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
use system::{GraphicsDevice, PciBus};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub struct CtrlGraphics {
|
||||
bus: PciBus,
|
||||
_amd: Vec<GraphicsDevice>,
|
||||
_intel: Vec<GraphicsDevice>,
|
||||
nvidia: Vec<GraphicsDevice>,
|
||||
#[allow(dead_code)]
|
||||
other: Vec<GraphicsDevice>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
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<()>;
|
||||
}
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl Dbus for CtrlGraphics {
|
||||
fn vendor(&self) -> String {
|
||||
self.get_gfx_mode()
|
||||
.map(|gfx| gfx.into())
|
||||
.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) {
|
||||
info!("Switching gfx mode to {}", vendor);
|
||||
let msg = self.set_gfx_config(tmp).unwrap_or_else(|err| {
|
||||
error!("{}", err);
|
||||
format!("Failed: {}", err.to_string())
|
||||
});
|
||||
self.notify_gfx(&vendor)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_action(&msg)
|
||||
.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 ZbusAdd for CtrlGraphics {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(
|
||||
&"/org/asuslinux/Gfx"
|
||||
.try_into()
|
||||
.expect("Couldn't add to zbus"),
|
||||
self,
|
||||
)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlGraphics: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl Reloadable for CtrlGraphics {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
self.auto_power()?;
|
||||
info!("Reloaded gfx mode: {:?}", self.get_gfx_mode()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlGraphics {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CtrlGraphics {
|
||||
bus,
|
||||
_amd: amd,
|
||||
_intel: intel,
|
||||
nvidia,
|
||||
other,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
fn save_gfx_mode(&self, vendor: GfxVendors) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.lock() {
|
||||
config.gfx_mode = vendor.clone();
|
||||
config.write();
|
||||
return Ok(());
|
||||
}
|
||||
// TODO: Error here
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Associated method to get which vendor mode is set
|
||||
pub fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> {
|
||||
if let Ok(config) = self.config.lock() {
|
||||
return Ok(config.gfx_mode.clone());
|
||||
}
|
||||
// TODO: Error here
|
||||
Ok(GfxVendors::Hybrid)
|
||||
}
|
||||
|
||||
fn get_runtime_status() -> Result<String, RogError> {
|
||||
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| RogError::Read(PATH.into(), err))?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> {
|
||||
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| RogError::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
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> {
|
||||
let text = if vendor == GfxVendors::Nvidia {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
||||
} else {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
|
||||
};
|
||||
|
||||
if !Path::new(XORG_PATH).exists() {
|
||||
std::fs::create_dir(XORG_PATH).map_err(|err| RogError::Write(XORG_PATH.into(), err))?;
|
||||
}
|
||||
|
||||
let file = XORG_PATH.to_string().add(XORG_FILE);
|
||||
info!("Writing {}", file);
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(&file)
|
||||
.map_err(|err| RogError::Write(file, err))?;
|
||||
|
||||
file.write_all(&text)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_modprobe_conf() -> Result<(), RogError> {
|
||||
info!("Writing {}", MODPROBE_PATH);
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(MODPROBE_PATH)
|
||||
.map_err(|err| RogError::Path(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
file.write_all(MODPROBE_BASE)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unbind_remove_nvidia(&self) -> Result<(), RogError> {
|
||||
// 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| RogError::Command("device unbind error".into(), err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_driver_action(driver: &str, action: &str) -> Result<(), RogError> {
|
||||
let mut cmd;
|
||||
if Self::kmod_exists() {
|
||||
info!("using kmod");
|
||||
cmd = Command::new("kmod");
|
||||
cmd.arg(action);
|
||||
} else {
|
||||
cmd = Command::new(action);
|
||||
}
|
||||
cmd.arg(driver);
|
||||
|
||||
let output = cmd
|
||||
.output()
|
||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
||||
if !output.status.success() {
|
||||
if output.stderr.ends_with("is not currently loaded\n".as_bytes()) {
|
||||
return Ok(())
|
||||
}
|
||||
if output.stderr.ends_with("Permission denied\n".as_bytes()) {
|
||||
let msg = format!("{} {} failed: {:?}", action, driver, String::from_utf8_lossy(&output.stderr));
|
||||
warn!("{}", msg);
|
||||
warn!("It may be safe to ignore the above error, run `lsmod |grep nvidia` to confirm modules loaded");
|
||||
return Ok(())
|
||||
}
|
||||
let msg = format!("{} {} failed: {:?}", action, driver, String::from_utf8_lossy(&output.stderr));
|
||||
return Err(RogError::Modprobe(msg));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_display_manager_action(action: &str) -> Result<(), RogError> {
|
||||
let mut cmd = Command::new("systemctl");
|
||||
cmd.arg(action);
|
||||
cmd.arg(DISPLAY_MANAGER);
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
||||
if !status.success() {
|
||||
let msg = format!(
|
||||
"systemctl {} {} failed: {:?}",
|
||||
action, DISPLAY_MANAGER, status
|
||||
);
|
||||
return Err(GfxError::DisplayManager(msg).into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_display_manager_inactive() -> Result<(), RogError> {
|
||||
let mut cmd = Command::new("systemctl");
|
||||
cmd.arg("is-active");
|
||||
cmd.arg(DISPLAY_MANAGER);
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
while count <= 4 {
|
||||
let output = cmd
|
||||
.output()
|
||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
||||
if output.stdout.starts_with("inactive".as_bytes()) {
|
||||
return Ok(());
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
count += 1;
|
||||
}
|
||||
return Err(
|
||||
GfxError::DisplayManager("display-manager did not completely stop".into()).into(),
|
||||
);
|
||||
}
|
||||
|
||||
fn kmod_exists() -> bool {
|
||||
let mut cmd = Command::new("which");
|
||||
cmd.arg("kmod");
|
||||
if let Ok(output) = cmd
|
||||
.output() {
|
||||
return output.status.success() && output.stdout.ends_with("kmod".as_bytes())
|
||||
}
|
||||
//Path::new("/usr/bin/kmod").exists()
|
||||
false
|
||||
}
|
||||
|
||||
pub fn do_vendor_tasks(&mut self, vendor: GfxVendors) -> Result<(), RogError> {
|
||||
Self::write_xorg_conf(vendor)?;
|
||||
Self::write_modprobe_conf()?; // TODO: Not required here, should put in startup?
|
||||
|
||||
// Rescan before doing remove or add drivers
|
||||
self.bus
|
||||
.rescan()
|
||||
.map_err(|err| GfxError::Bus("bus rescan error".into(), err))?;
|
||||
|
||||
match vendor {
|
||||
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Compute => {
|
||||
for driver in NVIDIA_DRIVERS.iter() {
|
||||
Self::do_driver_action(driver, "modprobe")?;
|
||||
}
|
||||
}
|
||||
// TODO: compute mode, needs different setup
|
||||
// GfxVendors::Compute => {}
|
||||
GfxVendors::Integrated => {
|
||||
for driver in NVIDIA_DRIVERS.iter() {
|
||||
Self::do_driver_action(driver, "rmmod")?;
|
||||
}
|
||||
self.unbind_remove_nvidia()?;
|
||||
}
|
||||
}
|
||||
|
||||
self.save_gfx_mode(vendor)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For manually calling (not on boot/startup)
|
||||
///
|
||||
/// Will stop and start display manager without warning
|
||||
pub fn set_gfx_config(&mut self, vendor: GfxVendors) -> Result<String, RogError> {
|
||||
Self::do_display_manager_action("stop")?;
|
||||
Self::wait_display_manager_inactive()?;
|
||||
self.do_vendor_tasks(vendor)?;
|
||||
Self::do_display_manager_action("start")?;
|
||||
// TODO: undo if failed? Save last mode, catch errors...
|
||||
let v: &str = vendor.into();
|
||||
Ok(format!("Graphics mode changed to {} successfully", v))
|
||||
}
|
||||
|
||||
// if CtrlRogBios::has_dedicated_gfx_toggle() {
|
||||
// if let Ok(config) = self.config.clone().try_lock() {
|
||||
// // Switch to dedicated if config says to do so
|
||||
// if config.gfx_nv_mode_is_dedicated && vendor == GfxVendors::Nvidia {
|
||||
// CtrlRogBios::set_gfx_mode(true)
|
||||
// .unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
||||
// } else if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
||||
// // otherwise if switching to non-Nvidia mode turn off dedicated mode
|
||||
// if ded == 1 && vendor != GfxVendors::Nvidia {
|
||||
// CtrlRogBios::set_gfx_mode(false)
|
||||
// .unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn auto_power(&mut self) -> Result<(), RogError> {
|
||||
let vendor = self.get_gfx_mode()?;
|
||||
self.do_vendor_tasks(vendor)?;
|
||||
Self::toggle_fallback_service(vendor)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
37
daemon/src/ctrl_gfx/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
pub mod error;
|
||||
|
||||
pub mod gfx;
|
||||
|
||||
pub mod system;
|
||||
|
||||
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];
|
||||
|
||||
const DISPLAY_MANAGER: &str = "display-manager.service";
|
||||
|
||||
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
|
||||
|
||||
static MODPROBE_BASE: &[u8] = br#"# Automatically generated by asusd
|
||||
#blacklist i2c_nvidia_gpu
|
||||
#alias i2c_nvidia_gpu off
|
||||
blacklist nouveau
|
||||
alias nouveau off
|
||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
||||
options nvidia-drm modeset=1
|
||||
"#;
|
||||
|
||||
const XORG_FILE: &str = "90-nvidia-primary.conf";
|
||||
const XORG_PATH: &str = "/etc/X11/xorg.conf.d/";
|
||||
|
||||
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"#;
|
||||
154
daemon/src/ctrl_gfx/system.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
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",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Will rescan the device tree, which adds all removed devices back
|
||||
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: 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 rebind(&self) -> Result<(), std::io::Error> {
|
||||
for func in self.functions.iter() {
|
||||
if func.path().exists() {
|
||||
match func.driver() {
|
||||
Ok(driver) => {
|
||||
info!("{}: Binding {}", driver.id(), func.id());
|
||||
unsafe {
|
||||
driver.bind(&func).map_err(|err| {
|
||||
error!("gfx bind: {}", 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(())
|
||||
}
|
||||
}
|
||||
569
daemon/src/ctrl_leds.rs
Normal file
@@ -0,0 +1,569 @@
|
||||
// Only these two packets must be 17 bytes
|
||||
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];
|
||||
|
||||
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
error::RogError,
|
||||
laptops::{match_laptop, HELP_ADDRESS},
|
||||
};
|
||||
use log::{error, info, warn};
|
||||
use rog_types::{
|
||||
aura_brightness_bytes,
|
||||
aura_modes::{AuraModes, PER_KEY},
|
||||
fancy::KeyColourArray,
|
||||
LED_MSG_LEN,
|
||||
};
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::{convert::TryInto, path::Path};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::GetSupported;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LedSupportedFunctions {
|
||||
pub brightness_set: bool,
|
||||
pub stock_led_modes: Option<Vec<u8>>,
|
||||
pub per_key_led_mode: bool,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlKbdBacklight {
|
||||
type A = LedSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
// let mode = <&str>::from(&<AuraModes>::from(*mode));
|
||||
let mut stock_led_modes = None;
|
||||
let mut per_key_led_mode = false;
|
||||
if let Some(laptop) = match_laptop() {
|
||||
let modes = laptop.supported_modes().to_vec();
|
||||
if modes.contains(&PER_KEY) {
|
||||
per_key_led_mode = true;
|
||||
let modes = modes.iter().filter(|x| **x != PER_KEY).copied().collect();
|
||||
stock_led_modes = Some(modes);
|
||||
} else {
|
||||
stock_led_modes = Some(modes);
|
||||
}
|
||||
}
|
||||
|
||||
LedSupportedFunctions {
|
||||
brightness_set: CtrlKbdBacklight::get_kbd_bright_path().is_ok(),
|
||||
stock_led_modes,
|
||||
per_key_led_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlKbdBacklight {
|
||||
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>>,
|
||||
}
|
||||
|
||||
pub struct DbusKbdBacklight {
|
||||
inner: Arc<Mutex<CtrlKbdBacklight>>,
|
||||
}
|
||||
|
||||
impl DbusKbdBacklight {
|
||||
pub fn new(inner: Arc<Mutex<CtrlKbdBacklight>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
trait Dbus {
|
||||
fn set_led(&mut self, data: String);
|
||||
fn ledmode(&self) -> String;
|
||||
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for DbusKbdBacklight {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Led".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
error!("DbusKbdBacklight: add_to_server {}", err);
|
||||
})
|
||||
.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(_) => {
|
||||
ctrl.do_command(data, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
_ => {
|
||||
if let Ok(json) = serde_json::to_string(&data) {
|
||||
match ctrl.do_command(data, &mut cfg) {
|
||||
Ok(_) => {
|
||||
self.notify_led(&json).ok();
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
condev_iface: Option<&String>,
|
||||
supported_modes: Vec<u8>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
) -> Result<Self, RogError> {
|
||||
// TODO: return error if *all* nodes are None
|
||||
let led_node = Self::get_node_failover(id_product, None, Self::scan_led_node).map_or_else(
|
||||
|err| {
|
||||
warn!("led_node: {}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
let kbd_node = Self::get_node_failover(id_product, condev_iface, Self::scan_kbd_node)
|
||||
.map_or_else(
|
||||
|err| {
|
||||
warn!("kbd_node: {}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
let bright_node = Self::get_kbd_bright_path();
|
||||
|
||||
if led_node.is_none() && kbd_node.is_none() && Self::get_kbd_bright_path().is_err() {
|
||||
return Err(RogError::MissingFunction(
|
||||
"All keyboard features missing, you may require a v5.11 series kernel or newer"
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ctrl = CtrlKbdBacklight {
|
||||
// Using `ok` here so we can continue without keyboard features but
|
||||
// still get brightness control at least... maybe...
|
||||
led_node,
|
||||
kbd_node,
|
||||
// TODO: Check for existance
|
||||
bright_node: bright_node?.to_owned(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
config,
|
||||
};
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_node_failover(
|
||||
id_product: &str,
|
||||
iface: Option<&String>,
|
||||
fun: fn(&str, Option<&String>) -> Result<String, RogError>,
|
||||
) -> Result<String, RogError> {
|
||||
match fun(id_product, iface) {
|
||||
Ok(o) => return Ok(o),
|
||||
Err(e) => {
|
||||
warn!("Looking for node: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
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_else(|| 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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, 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()
|
||||
.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 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
))
|
||||
}
|
||||
|
||||
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]
|
||||
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) {
|
||||
// println!("write: {:02x?}", &message);
|
||||
return file
|
||||
.write_all(message)
|
||||
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||
}
|
||||
}
|
||||
Err(RogError::NotSupported)
|
||||
}
|
||||
|
||||
/// Write an effect block
|
||||
#[inline]
|
||||
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)?;
|
||||
}
|
||||
} else {
|
||||
for row in effect.iter() {
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Used to set a builtin mode and save the settings for it
|
||||
///
|
||||
/// This needs to be universal so that settings applied by dbus stick
|
||||
#[inline]
|
||||
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)?;
|
||||
config.read();
|
||||
config.kbd_led_brightness = n;
|
||||
config.write();
|
||||
info!("LED brightness set to {:#?}", n);
|
||||
}
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes)?;
|
||||
} else {
|
||||
self.write_effect(&v)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
config.read();
|
||||
let mode_num: u8 = u8::from(&mode);
|
||||
self.write_mode(&mode)?;
|
||||
config.kbd_backlight_mode = mode_num;
|
||||
config.set_mode_data(mode);
|
||||
config.write();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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> {
|
||||
let mode_num: u8 = u8::from(mode);
|
||||
if !self.supported_modes.contains(&mode_num) {
|
||||
return Err(RogError::NotSupported);
|
||||
}
|
||||
match mode {
|
||||
AuraModes::PerKey(v) => {
|
||||
if v.is_empty() || v[0].is_empty() {
|
||||
let bytes = KeyColourArray::get_init_msg();
|
||||
self.write_bytes(&bytes)?;
|
||||
} else {
|
||||
self.write_effect(v)?;
|
||||
}
|
||||
}
|
||||
AuraModes::MultiStatic(_) | AuraModes::MultiBreathe(_) => {
|
||||
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
|
||||
for array in bytes.iter() {
|
||||
self.write_bytes(array)?;
|
||||
}
|
||||
self.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
self.write_bytes(&bytes)?;
|
||||
self.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
345
daemon/src/ctrl_rog_bios.rs
Normal file
@@ -0,0 +1,345 @@
|
||||
use crate::{config::Config, error::RogError, GetSupported};
|
||||
use log::{error, info, warn};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::{convert::TryInto, io::BufRead};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||
const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||
|
||||
static ASUS_SWITCH_GRAPHIC_MODE: &str =
|
||||
"/sys/firmware/efi/efivars/AsusSwitchGraphicMode-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||
static ASUS_POST_LOGO_SOUND: &str =
|
||||
"/sys/firmware/efi/efivars/AsusPostLogoSound-607005d5-3f75-4b2e-98f0-85ba66797a3e";
|
||||
|
||||
pub struct CtrlRogBios {
|
||||
_config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RogBiosSupportedFunctions {
|
||||
pub post_sound_toggle: bool,
|
||||
pub dedicated_gfx_toggle: bool,
|
||||
}
|
||||
|
||||
impl GetSupported for CtrlRogBios {
|
||||
type A = RogBiosSupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
RogBiosSupportedFunctions {
|
||||
post_sound_toggle: CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND).is_ok(),
|
||||
dedicated_gfx_toggle: CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl CtrlRogBios {
|
||||
pub fn set_dedicated_graphic_mode(&mut self, dedicated: bool) {
|
||||
self.set_gfx_mode(dedicated)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_asus_switch_graphic_mode {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
self.notify_dedicated_graphic_mode(dedicated)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: notify_asus_switch_graphic_mode {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn dedicated_graphic_mode(&self) -> i8 {
|
||||
Self::get_gfx_mode()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get_gfx_mode {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn notify_dedicated_graphic_mode(&self, dedicated: bool) -> zbus::Result<()> {}
|
||||
|
||||
// // // // // // // // // //
|
||||
|
||||
pub fn set_post_boot_sound(&mut self, on: bool) {
|
||||
Self::set_boot_sound(on)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: set_post_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
self.notify_post_boot_sound(on)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: notify_post_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn post_boot_sound(&self) -> i8 {
|
||||
Self::get_boot_sound()
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: get_boot_sound {}", err);
|
||||
err
|
||||
})
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
pub fn notify_post_boot_sound(&self, dedicated: bool) -> zbus::Result<()> {}
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for CtrlRogBios {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/RogBios".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlRogBios: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlRogBios {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlRogBios {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
match CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE) {
|
||||
Ok(_) => {
|
||||
CtrlRogBios::set_path_mutable(ASUS_SWITCH_GRAPHIC_MODE)?;
|
||||
}
|
||||
Err(err) => {
|
||||
info!("ROG Switchable Graphics (bios) not detected: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
match CtrlRogBios::check_path_exists(ASUS_POST_LOGO_SOUND) {
|
||||
Ok(_) => {
|
||||
CtrlRogBios::set_path_mutable(ASUS_POST_LOGO_SOUND)?;
|
||||
}
|
||||
Err(err) => {
|
||||
info!("ROG boot sound toggle (bios) not detected: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CtrlRogBios {
|
||||
_config: config,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_path_mutable(path: &str) -> Result<(), RogError> {
|
||||
let output = Command::new("/usr/bin/chattr")
|
||||
.arg("-i")
|
||||
.arg(path)
|
||||
.output()
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
info!("Set {} writeable: status: {}", path, output.status);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_path_exists(path: &str) -> Result<(), RogError> {
|
||||
if Path::new(path).exists() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RogError::MissingFunction(path.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_dedicated_gfx_toggle() -> bool {
|
||||
if CtrlRogBios::check_path_exists(ASUS_SWITCH_GRAPHIC_MODE).is_ok() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_gfx_mode() -> Result<i8, RogError> {
|
||||
let path = ASUS_SWITCH_GRAPHIC_MODE;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
Ok(data[idx] as i8)
|
||||
}
|
||||
|
||||
pub(super) fn set_gfx_mode(&self, dedicated: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_SWITCH_GRAPHIC_MODE;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data).unwrap();
|
||||
|
||||
let idx = data.len() - 1;
|
||||
if dedicated {
|
||||
data[idx] = 1;
|
||||
info!("Set system-level graphics mode: Dedicated Nvidia");
|
||||
} else {
|
||||
data[idx] = 0;
|
||||
info!("Set system-level graphics mode: Optimus");
|
||||
}
|
||||
file.write_all(&data)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
self.update_initramfs(dedicated)?;
|
||||
|
||||
// if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
||||
// if let Ok(vendor) = CtrlGraphics::get_vendor() {
|
||||
// if ded == 1 && vendor != "nvidia" {
|
||||
// warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
||||
// CtrlGraphics::set_gfx_config(&GfxVendors::Nvidia)
|
||||
// .unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_boot_sound() -> Result<i8, RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
Ok(data[idx] as i8)
|
||||
}
|
||||
|
||||
pub(super) fn set_boot_sound(on: bool) -> Result<(), RogError> {
|
||||
let path = ASUS_POST_LOGO_SOUND;
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)
|
||||
.map_err(|err| RogError::Read(path.into(), err))?;
|
||||
|
||||
let idx = data.len() - 1;
|
||||
if on {
|
||||
data[idx] = 1;
|
||||
info!("Set boot POST sound on");
|
||||
} else {
|
||||
data[idx] = 0;
|
||||
info!("Set boot POST sound off");
|
||||
}
|
||||
file.write_all(&data)
|
||||
.map_err(|err| RogError::Path(path.into(), err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// required for g-sync mode
|
||||
fn update_initramfs(&self, dedicated: bool) -> Result<(), RogError> {
|
||||
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'");
|
||||
}
|
||||
|
||||
if let Some(mut cmd) = initfs_cmd {
|
||||
info!("Updating initramfs");
|
||||
|
||||
// If switching to Nvidia dedicated we need these modules included
|
||||
if Path::new(DRACUT_PATH).exists() && dedicated {
|
||||
cmd.arg("--add-drivers");
|
||||
cmd.arg("nvidia nvidia-drm nvidia-modeset nvidia-uvm");
|
||||
info!("System uses dracut, forcing nvidia modules to be included in init");
|
||||
} else if Path::new(INITRAMFS_PATH).exists() {
|
||||
let modules = vec![
|
||||
"nvidia\n",
|
||||
"nvidia-drm\n",
|
||||
"nvidia-modeset\n",
|
||||
"nvidia-uvm\n",
|
||||
];
|
||||
|
||||
let module_include = Path::new("/etc/initramfs-tools/modules");
|
||||
|
||||
if dedicated {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
// add nvidia modules to module_include
|
||||
file.write_all(modules.concat().as_bytes()).unwrap();
|
||||
} else {
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
// remove modules
|
||||
for line in std::io::BufReader::new(file).lines() {
|
||||
if let Ok(l) = line {
|
||||
if !modules.contains(&l.as_ref()) {
|
||||
buf.append(&mut l.as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(module_include)
|
||||
.map_err(|err| {
|
||||
RogError::Write(module_include.to_string_lossy().to_string(), err)
|
||||
})?;
|
||||
std::io::BufWriter::new(file).write_all(&buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let status = cmd
|
||||
.status()
|
||||
.map_err(|err| RogError::Write(format!("{:?}", cmd), err))?;
|
||||
if !status.success() {
|
||||
error!("Ram disk update failed");
|
||||
return Err(RogError::Initramfs("Ram disk update failed".into()).into());
|
||||
} else {
|
||||
info!("Successfully updated initramfs");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
56
daemon/src/ctrl_supported.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use log::warn;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::{
|
||||
ctrl_anime::{AnimeSupportedFunctions, CtrlAnimeDisplay},
|
||||
ctrl_charge::{ChargeSupportedFunctions, CtrlCharge},
|
||||
ctrl_fan_cpu::{CtrlFanAndCPU, FanCpuSupportedFunctions},
|
||||
ctrl_leds::{CtrlKbdBacklight, LedSupportedFunctions},
|
||||
ctrl_rog_bios::{CtrlRogBios, RogBiosSupportedFunctions},
|
||||
GetSupported,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SupportedFunctions {
|
||||
anime_ctrl: AnimeSupportedFunctions,
|
||||
charge_ctrl: ChargeSupportedFunctions,
|
||||
fan_cpu_ctrl: FanCpuSupportedFunctions,
|
||||
keyboard_led: LedSupportedFunctions,
|
||||
rog_bios_ctrl: RogBiosSupportedFunctions,
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl SupportedFunctions {
|
||||
fn supported_functions(&self) -> String {
|
||||
serde_json::to_string_pretty(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for SupportedFunctions {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Supported".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("SupportedFunctions: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSupported for SupportedFunctions {
|
||||
type A = SupportedFunctions;
|
||||
|
||||
fn get_supported() -> Self::A {
|
||||
SupportedFunctions {
|
||||
keyboard_led: CtrlKbdBacklight::get_supported(),
|
||||
anime_ctrl: CtrlAnimeDisplay::get_supported(),
|
||||
charge_ctrl: CtrlCharge::get_supported(),
|
||||
fan_cpu_ctrl: CtrlFanAndCPU::get_supported(),
|
||||
rog_bios_ctrl: CtrlRogBios::get_supported(),
|
||||
}
|
||||
}
|
||||
}
|
||||
196
daemon/src/daemon.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use daemon::ctrl_charge::CtrlCharge;
|
||||
use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu};
|
||||
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
|
||||
use daemon::laptops::match_laptop;
|
||||
use daemon::{
|
||||
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||
};
|
||||
use daemon::{ctrl_anime::CtrlAnimeDisplay, ctrl_gfx::gfx::CtrlGraphics};
|
||||
|
||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||
use log::LevelFilter;
|
||||
use log::{error, info, warn};
|
||||
use rog_dbus::DBUS_NAME;
|
||||
use rog_types::gfx_vendors::GfxVendors;
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use daemon::ctrl_rog_bios::CtrlRogBios;
|
||||
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)
|
||||
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
|
||||
.filter(None, LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
info!(" daemon v{}", daemon::VERSION);
|
||||
info!(" rog-dbus v{}", rog_dbus::VERSION);
|
||||
info!("rog-types v{}", rog_types::VERSION);
|
||||
|
||||
start_daemon()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Timing is such that:
|
||||
// - interrupt write is minimum 1ms (sometimes lower)
|
||||
// - read interrupt must timeout, minimum of 1ms
|
||||
// - for a single usb packet, 2ms total.
|
||||
// - to maintain constant times of 1ms, per-key colours should use
|
||||
// the effect endpoint so that the complete colour block is written
|
||||
// as fast as 1ms per row of the matrix inside it. (10ms total time)
|
||||
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
let supported = SupportedFunctions::get_supported();
|
||||
print_board_info();
|
||||
println!("{}", serde_json::to_string_pretty(&supported).unwrap());
|
||||
|
||||
let laptop = match_laptop();
|
||||
let config = if let Some(laptop) = laptop.as_ref() {
|
||||
Config::load(laptop.supported_modes())
|
||||
} else {
|
||||
Config::load(&[])
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
supported.add_to_server(&mut object_server);
|
||||
|
||||
let enable_gfx_switching = config.gfx_managed;
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
|
||||
match CtrlRogBios::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!("rog_bios_control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
match CtrlAnimeDisplay::new() {
|
||||
Ok(ctrl) => {
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("AniMe control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if enable_gfx_switching {
|
||||
match CtrlGraphics::new(config.clone()) {
|
||||
Ok(mut ctrl) => {
|
||||
// Need to check if a laptop has the dedicated gfx switch
|
||||
if CtrlRogBios::has_dedicated_gfx_toggle() {
|
||||
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
||||
if let Ok(vendor) = ctrl.get_gfx_mode() {
|
||||
if ded == 1 && vendor != GfxVendors::Nvidia {
|
||||
error!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
||||
error!("You must reboot to enable Nvidia driver");
|
||||
ctrl.do_vendor_tasks(GfxVendors::Nvidia)?;
|
||||
} else if ded == 0 {
|
||||
info!("Dedicated GFX toggle is off");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| error!("Gfx controller: {}", err));
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Gfx control: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect tasks for task thread
|
||||
let mut tasks: Vec<Arc<Mutex<dyn CtrlTask + Send>>> = Vec::new();
|
||||
|
||||
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).add_to_server(&mut object_server);
|
||||
};
|
||||
|
||||
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);
|
||||
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)
|
||||
})
|
||||
.map_err(|err| {
|
||||
warn!("object_server notify_charge error: {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
loop {
|
||||
if let Err(err) = object_server.try_handle_next() {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
daemon/src/error.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use intel_pstate::PStateError;
|
||||
use rog_fan_curve::CurveError;
|
||||
use rog_types::error::GraphicsError;
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
|
||||
use crate::ctrl_gfx::error::GfxError;
|
||||
|
||||
#[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),
|
||||
ReloadFail(String),
|
||||
GfxSwitching(GfxError),
|
||||
Initramfs(String),
|
||||
Modprobe(String),
|
||||
Command(String, std::io::Error),
|
||||
}
|
||||
|
||||
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 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),
|
||||
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
|
||||
RogError::GfxSwitching(deets) => write!(f, "Graphics switching error: {}", deets),
|
||||
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
|
||||
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
||||
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GraphicsError> for RogError {
|
||||
fn from(err: GraphicsError) -> Self {
|
||||
match err {
|
||||
GraphicsError::ParseVendor => RogError::GfxSwitching(GfxError::ParseVendor),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
use asus_nb::aura_modes::{AuraModes, BREATHING, STATIC, STROBE};
|
||||
use log::{info, warn};
|
||||
use rog_types::aura_modes::{AuraModes, BREATHING, STATIC};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::OpenOptions;
|
||||
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";
|
||||
|
||||
static LAPTOP_DEVICES: [u16; 4] = [0x1866, 0x1869, 0x1854, 0x19b6];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LaptopBase {
|
||||
usb_product: String,
|
||||
condev_iface: Option<String>, // required for finding the Consumer Device interface
|
||||
supported_modes: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -17,47 +21,52 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_laptop() -> Option<LaptopBase> {
|
||||
for device in rusb::devices().unwrap().iter() {
|
||||
let device_desc = device.device_descriptor().unwrap();
|
||||
for device in rusb::devices().expect("Couldn't get device").iter() {
|
||||
let device_desc = device
|
||||
.device_descriptor()
|
||||
.expect("Couldn't get device descriptor");
|
||||
if device_desc.vendor_id() == 0x0b05 {
|
||||
match device_desc.product_id() {
|
||||
0x1866 => {
|
||||
let laptop = select_1866_device("1866".to_owned());
|
||||
print_modes(&laptop.supported_modes);
|
||||
if LAPTOP_DEVICES.contains(&device_desc.product_id()) {
|
||||
let prod_str = format!("{:x?}", device_desc.product_id());
|
||||
|
||||
if device_desc.product_id() == 0x1854 {
|
||||
let mut laptop = laptop(prod_str, None);
|
||||
if laptop.supported_modes.is_empty() {
|
||||
laptop.supported_modes = vec![STATIC, BREATHING];
|
||||
}
|
||||
return Some(laptop);
|
||||
}
|
||||
0x1869 => return Some(select_1866_device("1869".to_owned())),
|
||||
0x1854 => {
|
||||
info!("Found GL753 or similar");
|
||||
return Some(LaptopBase {
|
||||
usb_product: "1854".to_string(),
|
||||
supported_modes: vec![STATIC, BREATHING, STROBE],
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
|
||||
let laptop = laptop(prod_str, Some("02".to_owned()));
|
||||
return Some(laptop);
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
None
|
||||
}
|
||||
|
||||
fn select_1866_device(prod: String) -> LaptopBase {
|
||||
fn laptop(prod: String, condev_iface: Option<String>) -> LaptopBase {
|
||||
let dmi = sysfs_class::DmiId::default();
|
||||
let board_name = dmi.board_name().expect("Could not get board_name");
|
||||
let prod_family = dmi.product_family().expect("Could not get product_family");
|
||||
let prod_name = dmi.product_name().expect("Could not get product_name");
|
||||
|
||||
info!("Product name: {}", prod_name.trim());
|
||||
info!("Board name: {}", board_name.trim());
|
||||
|
||||
let mut laptop = LaptopBase {
|
||||
usb_product: prod,
|
||||
condev_iface,
|
||||
supported_modes: vec![],
|
||||
};
|
||||
|
||||
@@ -67,17 +76,21 @@ fn select_1866_device(prod: String) -> LaptopBase {
|
||||
return laptop;
|
||||
}
|
||||
}
|
||||
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
|
||||
laptop
|
||||
}
|
||||
|
||||
fn print_modes(supported_modes: &[u8]) {
|
||||
pub fn print_board_info() {
|
||||
let dmi = sysfs_class::DmiId::default();
|
||||
let board_name = dmi.board_name().expect("Could not get board_name");
|
||||
let prod_name = dmi.product_name().expect("Could not get product_name");
|
||||
let prod_family = dmi.product_family().expect("Could not get product_family");
|
||||
|
||||
info!("Product name: {}", prod_name.trim());
|
||||
info!("Product family: {}", prod_family.trim());
|
||||
info!("Board name: {}", board_name.trim());
|
||||
}
|
||||
|
||||
pub fn print_modes(supported_modes: &[u8]) {
|
||||
if !supported_modes.is_empty() {
|
||||
info!("Supported Keyboard LED modes are:");
|
||||
for mode in supported_modes {
|
||||
63
daemon/src/lib.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
#![deny(unused_must_use)]
|
||||
/// Configuration loading, saving
|
||||
pub mod config;
|
||||
pub(crate) mod config_old;
|
||||
/// Control of AniMe matrix display
|
||||
pub mod ctrl_anime;
|
||||
/// Control of battery charge level
|
||||
pub mod ctrl_charge;
|
||||
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
||||
///
|
||||
/// Intel machines can control:
|
||||
/// - CPU min/max frequency
|
||||
/// - CPU turbo enable/disable
|
||||
/// - Fan mode (normal, boost, silent)
|
||||
///
|
||||
/// AMD machines can control:
|
||||
/// - CPU turbo enable/disable
|
||||
/// - Fan mode (normal, boost, silent)
|
||||
/// - Fan min/max RPM curve
|
||||
pub mod ctrl_fan_cpu;
|
||||
/// GPU switching and power
|
||||
pub mod ctrl_gfx;
|
||||
/// Keyboard LED brightness control, RGB, and LED display modes
|
||||
pub mod ctrl_leds;
|
||||
/// Control ASUS bios function such as boot sound, Optimus/Dedicated gfx mode
|
||||
pub mod ctrl_rog_bios;
|
||||
/// Laptop matching to determine capabilities
|
||||
pub mod laptops;
|
||||
|
||||
/// Fetch all supported functions for the laptop
|
||||
pub mod ctrl_supported;
|
||||
|
||||
mod error;
|
||||
|
||||
use crate::error::RogError;
|
||||
use config::Config;
|
||||
use zbus::ObjectServer;
|
||||
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub trait Reloadable {
|
||||
fn reload(&mut self) -> Result<(), RogError>;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
fn do_task(&mut self, config: &mut Config, event: Self::A);
|
||||
}
|
||||
|
||||
pub trait GetSupported {
|
||||
type A;
|
||||
|
||||
fn get_supported() -> Self::A;
|
||||
}
|
||||
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
@@ -0,0 +1,4 @@
|
||||
Section "ServerLayout"
|
||||
Identifier "layout"
|
||||
Option "AllowNVIDIAGPUScreens"
|
||||
EndSection
|
||||
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
@@ -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
@@ -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
|
||||
@@ -3,6 +3,21 @@ prod_family = "Zephyrus S"
|
||||
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 = "Zephyrus M"
|
||||
board_names = ["GU502GV"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Zephyrus M15"
|
||||
board_names = ["GU502LW"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Zephyrus M15"
|
||||
board_names = ["GU502LU"]
|
||||
led_modes = [0, 1, 2, 10]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Zephyrus"
|
||||
board_names = ["GM501GM", "GX531"]
|
||||
@@ -16,17 +31,17 @@ 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 = [0, 1, 2, 3, 10, 13, 14]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG Strix"
|
||||
board_names = ["G512LI", "G712LI"]
|
||||
board_names = ["G512LI", "G712LI", "G531GD"]
|
||||
led_modes = [0, 1, 2, 3, 10]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Strix"
|
||||
board_names = ["G731GV", "G731GW", "G531GV"]
|
||||
led_modes = [0, 1, 2, 3, 13]
|
||||
led_modes = [0, 1, 2, 3, 13, 14]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "Strix"
|
||||
@@ -36,4 +51,9 @@ led_modes = [0, 1, 2, 3]
|
||||
[[led_modes]]
|
||||
prod_family = "Strix Scar"
|
||||
board_names = ["G531", "G731"]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 255]
|
||||
led_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 255]
|
||||
|
||||
[[led_modes]]
|
||||
prod_family = "ROG"
|
||||
board_names = ["GL553VE"]
|
||||
led_modes = [0, 1, 2, 13, 14]
|
||||
@@ -10,6 +10,10 @@
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
</policy>
|
||||
<policy group="users">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
</policy>
|
||||
<policy group="wheel">
|
||||
<allow send_destination="org.asuslinux.Daemon"/>
|
||||
<allow receive_sender="org.asuslinux.Daemon"/>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="18[0-9][0-9]", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
|
||||
ACTION=="add|change", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", ENV{ID_TYPE}=="hid", TAG+="systemd", ENV{SYSTEMD_WANTS}="asusd.service"
|
||||
ACTION=="add|remove", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="0b05", ENV{ID_MODEL_ID}=="1[89][a-zA-Z0-9][a-zA-Z0-9]|193b", RUN+="systemctl restart asusd.service"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Description=ASUS Notebook Control
|
||||
StartLimitInterval=200
|
||||
StartLimitBurst=2
|
||||
Before=display-manager.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/asusd
|
||||
|
||||
BIN
data/icons/asus_notif_blue.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
data/icons/asus_notif_green.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
data/icons/asus_notif_orange.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
data/icons/asus_notif_red.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
data/icons/asus_notif_white.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
data/icons/asus_notif_yellow.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
@@ -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
|
||||
|
||||
|
||||
@@ -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]#
|
||||
@@ -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
|
||||
|
||||