Compare commits

...

55 Commits
1.1.2 ... 2.2.1

Author SHA1 Message Date
Luke Jones
b3a555cab9 Merge branch 'fluke/asus_bios_settings' into 'next'
Bugfixes and improvements

Closes #48

See merge request asus-linux/asus-nb-ctrl!17
2021-01-27 01:16:39 +00:00
Luke
cf13b4f71b Bugfixes and improvements
- fix CLI feedback for reboot/restartx. Update readme
- dracut force driver include for nvidia dedicated
- change fan-mode CLI tag

Closes #48
2021-01-27 14:13:02 +13:00
Luke Jones
cd0b9fe350 Merge branch 'fluke/asus_bios_settings' into 'next'
Fluke/asus bios settings

See merge request asus-linux/asus-nb-ctrl!15
2021-01-26 08:08:13 +00:00
Luke
82900f4645 CLI args for bios. Cleanup and improve
- dbus method for 'supported modes'
- add dedicated gfx safety
- bring ctrl-gfx back in to main control for better integration
- safely upgrade config files
2021-01-26 21:07:19 +13:00
Luke
703bba9ffd Correct changelog 2021-01-10 22:34:45 +13:00
Luke Jones
73706154a4 Merge branch 'fluke/fixes' into 'next'
Fixes

See merge request asus-linux/asus-nb-ctrl!14
2021-01-10 09:31:46 +00:00
Luke
c9b2a0c777 Fixes
- Adjust gfx controller to assume that the graphics driver is loaded if the
  mode is set for nvidia/hybrid
- Small code adjustments for error handling
2021-01-10 22:27:56 +13:00
Luke
54cc51fe5d Minor update to deps 2021-01-09 13:48:13 +13:00
Luke
81645d0777 Update deps and fmt 2021-01-09 13:42:46 +13:00
Luke Jones
d61c180ee5 Update discord link 2020-12-20 21:21:53 +00:00
Luke Jones
a668800fd9 Merge branch 'asere/anime_better_options' into 'next'
Better anime options & gumdrop error Display

See merge request asus-linux/asus-nb-ctrl!12
2020-12-20 21:18:51 +00:00
Asere
3bdc11c994 Better anime options & gumdrop error Display 2020-11-25 18:37:36 +01:00
Luke Jones
b496139063 Merge branch 'StaticRocket-next-patch-05672' into 'next'
Update paths and variables to follow GNU Make standards

See merge request asus-linux/asus-nb-ctrl!13
2020-11-08 00:17:44 +00:00
Randolph Sapp
607483629a Don't rebuild during install
Rebuild during all
2020-11-08 00:17:44 +00:00
Luke D Jones
5c8d138cef Update readme 2020-10-25 20:37:53 +13:00
Luke D Jones
e3db1f7c4c Add notification dbus signal for aura next/prev 2020-10-25 19:34:40 +13:00
Luke D Jones
de3b803f14 Add DBUS methods to toggle next/previous aura mode 2020-10-25 15:49:14 +13:00
Luke D Jones
0558f919c4 Add DBUS method to toggle to next profile 2020-10-25 15:02:35 +13:00
Luke Jones
68ea73c847 Merge branch 'asere/anime_on_off' into 'next'
AniMe: adding --on and --off options to turn on/off (and accept/reject write requests)

See merge request asus-linux/asus-nb-ctrl!11
2020-10-25 01:14:12 +00:00
Luke Jones
96ddb7132d Merge branch 'asere/show_keyboard_brightness' into 'next'
Improving user experience by showing current keyboard led brightness

See merge request asus-linux/asus-nb-ctrl!10
2020-10-25 01:12:21 +00:00
Asere
d36ac44603 updating option "-k" to show current brightness: asusctl -k 2020-10-23 02:00:08 +02:00
Asere
5d06c87943 AniMe: adding --on and --off options to turn on/off (and accept/reject write requests) 2020-10-22 14:09:05 +02:00
Luke D Jones
588e3c0102 Panic if config file is bad 2020-10-22 08:28:12 +13:00
Luke D Jones
6ce32c1cab Fix one super silly bug
closes #30
2020-10-22 08:16:00 +13:00
Luke Jones
a23c51e5db Adding Anime commands to asusctl 2020-10-11 22:46:06 +00:00
Asere
3845071ba3 adding Anime feature: led brightness command to asusctl, and updating dbus client 2020-10-12 00:11:26 +02:00
Luke Jones
a48b3634bf Merge branch 'next' into 'next'
Add some basic zsh completions for asusctl

See merge request asus-linux/asus-nb-ctrl!8
2020-10-05 00:38:34 +00:00
Luke Jones
806882b2e9 Merge branch 'next' into 'next'
fixed fan presets for profiles

See merge request asus-linux/asus-nb-ctrl!7
2020-10-05 00:37:43 +00:00
Luke D Jones
9ac3c46fe6 Actually enable/disable gfx switch control 2020-10-05 09:32:46 +13:00
Luke D Jones
6817a1c027 Bump dbus autogen 2020-10-05 09:31:07 +13:00
Dylan Jones
cf2be1b12b Add some basic zsh completions for asusctl 2020-09-30 13:28:05 -04:00
Luke D Jones
9ffeb19c8c Bump version 2020-09-29 21:30:23 +13:00
Cuong
a7f6e8bd24 fixed fan presets for profiles 2020-09-28 10:04:57 +00:00
Luke Jones
f61f62c219 Merge branch 'feature/dbus-profile-name' into 'next'
 Add ActiveProfileName() dbus method returning name of current profile

See merge request asus-linux/asus-nb-ctrl!5
2020-09-26 08:50:03 +00:00
Andreas Streichardt
bb70fd7b71 Add ActiveProfileName() dbus method returning name of current profile 2020-09-26 09:05:50 +02:00
Luke D Jones
b80c860b7a Fix args to systemctl for reboot 2020-09-25 21:05:47 +12:00
Luke D Jones
3c7544f034 Explicitly state arch is not supported 2020-09-25 14:14:21 +12:00
Luke D Jones
6746c2b654 Update changelog 2020-09-24 09:04:49 +12:00
Luke D Jones
8d59f89438 Add verbose error for LED node missing 2020-09-24 08:18:10 +12:00
Luke D Jones
5753160e91 Don't throw away do_task error, log it 2020-09-24 08:10:05 +12:00
Luke D Jones
622cd9d943 Fix timeout for GFX switching 2020-09-24 08:00:37 +12:00
Luke D Jones
2daa7f0811 Git internal version from crate version 2020-09-23 22:12:47 +12:00
Luke D Jones
05c53df0ed Better description of config panic 2020-09-23 21:24:25 +12:00
Luke D Jones
a7419cbc4c Remove many unwraps or change to maps 2020-09-23 20:47:33 +12:00
Luke D Jones
67ad38a7e6 Ditch the gsync line in screen settings file. Oops
- Add force option to `systemctl reboot`. Be careful.
2020-09-23 16:51:46 +12:00
Luke Jones
e572ae2c62 Update Makefile 2020-09-23 02:32:25 +00:00
Luke Jones
24962eedc1 Merge branch 'testing' into 'next'
Bugfixes and improvements

Closes #11

See merge request asus-linux/asus-nb-ctrl!4
2020-09-23 01:05:15 +00:00
Luke D Jones
368d279ca5 Add "info" output for gfx driver check 2020-09-23 12:59:15 +12:00
Luke D Jones
9e4cc329ed Add "info" output for gfx driver check 2020-09-22 09:54:37 +12:00
Luke D Jones
d4702a166a Support Zephyrus M GU502GV
closes #11
2020-09-22 09:47:20 +12:00
Luke D Jones
925c709097 Minor CLI output correction 2020-09-21 20:55:47 +12:00
Luke D Jones
411788f72c Remove triple-buffer line 2020-09-21 20:44:31 +12:00
Luke D Jones
4e4ea0035e Add missing gfx dbus signal 2020-09-21 20:41:28 +12:00
Luke Jones
0063f3f0fc Merge branch 'fluke/gfx_ctrl_zbus' into 'next'
Fluke/gfx ctrl zbus

See merge request asus-linux/asus-nb-ctrl!3
2020-09-20 22:41:44 +00:00
Luke D Jones
fe6231ad4e GFX control, no-tokio, no-async, dbus client refactor
- Working gfx modes <iGPU only, dGPU only, or hybrid>
- Add signal for gfx vendor change and make CLI wait for signal
- Add polling for led brightness to save to config
- Move daemon to zbus crate
- dbus client refactor
- Further dbus methods and updates
- Add basic notification user daemon and systemd service
2020-09-21 10:36:22 +12:00
140 changed files with 4582 additions and 363531 deletions

View File

@@ -6,6 +6,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
# [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

975
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[workspace]
members = ["asus-nb-ctrl", "asus-nb"]
members = ["asus-notify", "asus-nb-ctrl", "asus-nb"]
[profile.release]
lto = true
@@ -13,4 +13,4 @@ opt-level = 1
[profile.bench]
debug = false
opt-level = 3
opt-level = 3

View File

@@ -1,33 +1,37 @@
prefix ?= /usr
sysconfdir ?= /etc
VERSION := $(shell grep -Pm1 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2)
INSTALL = install
INSTALL_PROGRAM = ${INSTALL} -D -m 0755
INSTALL_DATA = ${INSTALL} -D -m 0644
prefix = /usr
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
libdir = $(exec_prefix)/lib
includedir = $(prefix)/include
datarootdir = $(prefix)/share
datadir = $(datarootdir)
libdir = $(exec_prefix)/lib
zshcpl = $(datarootdir)/zsh/site-functions
SRC = Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.rs')
BIN_C := asusctl
BIN_D := asusd
BIN_N := asus-notify
LEDCFG := asusd-ledmodes.toml
X11CFG := 90-nvidia-screen-G05.conf
PMRULES := 90-asusd-nvidia-pm.rules
.PHONY: all clean distclean install uninstall update
BIN_C=asusctl
BIN_D=asusd
LEDCONFIG=asusd-ledmodes.toml
VERSION:=$(shell grep -Pm1 '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
@@ -62,9 +83,11 @@ 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_asus-nb-ctrl_$(VERSION).tar.xz
endif
cargo build $(ARGS)
.PHONY: all clean distclean install uninstall update build

303
README.md
View File

@@ -1,10 +1,19 @@
# ASUS NB Ctrl
`asusd` is a utility for Linux to control many aspects of various ASUS laptops
but can also be used with non-asus laptops with reduced features.
**NOTICE:**
This program requires the kernel patch in `./kernel-patch/` to be applied.
As of 04/08/2020 these have been submitted to lkml. 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 +21,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 +124,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 libdbus-1-dev libclang-dev libudev-dev`
**fedora:** `dnf install clang-devel dbus-devel systemd-devel`
## Installing
@@ -123,6 +154,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 +169,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,79 +219,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
profile Create and configure profiles
$ asusctl profile --help
Usage: asusctl profile [OPTIONS]
Positional arguments:
profile
Optional arguments:
-h, --help print help message
-c, --create create the profile if it doesn't exist
-t, --turbo enable cpu turbo (AMD)
-n, --no-turbo disable cpu turbo (AMD)
-m, --min-percentage MIN-PERCENTAGE
set min cpu scaling (intel)
-M, --max-percentage MAX-PERCENTAGE
set max cpu scaling (intel)
-p, --preset PWR <silent, normal, boost>
-C, --curve CURVE set fan curve
$ 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:
@@ -268,27 +232,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)

View File

@@ -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
View 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

View File

@@ -1,6 +1,6 @@
[package]
name = "asus-nb-ctrl"
version = "1.1.2"
version = "2.2.1"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -25,17 +25,14 @@ path = "src/daemon.rs"
asus-nb = { path = "../asus-nb" }
rusb = "^0.6.0"
udev = "^0.4.0"
async-trait = "0.1.36"
# cli and logging
gumdrop = "^0.8.0"
log = "^0.4.8"
env_logger = "^0.7.1"
# async
dbus = { version = "^0.8.2", features = ["futures"] }
dbus-tokio = "^0.5.1"
tokio = { version = "^0.2.4", features = ["rt-threaded", "sync"] }
zbus = "^1.8.0"
zvariant = "^2.4.0"
# serialisation
serde = "^1.0"
@@ -48,3 +45,5 @@ sysfs-class = "^0.1.2" # used for backlight control and baord ID
rog_fan_curve = { version = "0.1.5", features = ["serde"] }
# cpu power management
intel-pstate = "^0.2.1"
yansi-term = "^0.1"

View File

@@ -1,15 +1,51 @@
use asus_nb::aura_modes::AuraModes;
use log::{error, warn};
use log::{error, info, warn};
use rog_fan_curve::Curve;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use crate::VERSION;
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
/// for parsing old v2.1.2 config
#[derive(Deserialize)]
struct ConfigV212 {
gfx_managed: bool,
active_profile: String,
toggle_profiles: Vec<String>,
// TODO: remove power_profile
power_profile: u8,
bat_charge_limit: u8,
kbd_led_brightness: u8,
kbd_backlight_mode: u8,
kbd_backlight_modes: Vec<AuraModes>,
power_profiles: BTreeMap<String, Profile>,
}
impl ConfigV212 {
fn into_current(self) -> Config {
Config {
gfx_managed: self.gfx_managed,
gfx_nv_mode_is_dedicated: true,
active_profile: self.active_profile,
toggle_profiles: self.toggle_profiles,
power_profile: 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(Default, Deserialize, Serialize)]
pub struct Config {
pub gfx_managed: bool,
pub gfx_nv_mode_is_dedicated: bool,
pub active_profile: String,
pub toggle_profiles: Vec<String>,
// TODO: remove power_profile
@@ -22,62 +58,75 @@ pub struct Config {
}
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 {
/// `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)
.unwrap(); // okay to cause panic here
.expect(&format!(
"The file {} or directory /etc/asusd/ is missing",
CONFIG_PATH
)); // okay to cause panic here
let mut buf = String::new();
if let Ok(l) = file.read_to_string(&mut buf) {
if l == 0 {
self = Config::create_default(&mut file, &supported_led_modes);
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 {
self = serde_json::from_str(&buf).unwrap_or_else(|_| {
warn!(
"Could not deserialise {}. Overwriting with default",
CONFIG_PATH
);
Config::create_default(&mut file, &supported_led_modes)
});
if let Ok(data) = serde_json::from_str(&buf) {
return data;
} 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);
}
}
self
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 c = Config::default();
c.bat_charge_limit = 100;
c.kbd_backlight_mode = 0;
c.kbd_led_brightness = 1;
let mut config = Config::default();
config.gfx_managed = true;
config.gfx_nv_mode_is_dedicated = true;
config.bat_charge_limit = 100;
config.kbd_backlight_mode = 0;
config.kbd_led_brightness = 1;
for n in supported_led_modes {
c.kbd_backlight_modes.push(AuraModes::from(*n))
config.kbd_backlight_modes.push(AuraModes::from(*n))
}
let profile = Profile::default();
c.power_profiles.insert("normal".into(), profile);
let mut profile = Profile::default();
profile.fan_preset = 0;
profile.turbo = true;
config.power_profiles.insert("normal".into(), profile);
let mut profile = Profile::default();
profile.fan_preset = 1;
c.power_profiles.insert("boost".into(), profile);
profile.turbo = true;
config.power_profiles.insert("boost".into(), profile);
let mut profile = Profile::default();
profile.fan_preset = 2;
c.power_profiles.insert("silent".into(), profile);
config.power_profiles.insert("silent".into(), profile);
c.toggle_profiles.push("normal".into());
c.toggle_profiles.push("boost".into());
c.toggle_profiles.push("silent".into());
c.active_profile = "normal".into();
config.toggle_profiles.push("normal".into());
config.toggle_profiles.push("boost".into());
config.toggle_profiles.push("silent".into());
config.active_profile = "normal".into();
// Should be okay to unwrap this as is since it is a Default
let json = serde_json::to_string_pretty(&c).unwrap();
let json = serde_json::to_string_pretty(&config).unwrap();
file.write_all(json.as_bytes())
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
c
config
}
pub fn read(&mut self) {
@@ -97,6 +146,17 @@ impl Config {
}
}
pub fn read_new() -> Result<Config, Box<dyn std::error::Error>> {
let mut file = OpenOptions::new()
.read(true)
.open(&CONFIG_PATH)
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
let mut buf = String::new();
file.read_to_string(&mut buf)?;
let x: Config = serde_json::from_str(&buf)?;
Ok(x)
}
pub fn write(&self) {
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");

View File

@@ -6,25 +6,41 @@ const DEV_PAGE: u8 = 0x5e;
// These bytes are in [1] position of the array
const WRITE: u8 = 0xc0;
const INIT: u8 = 0xc2;
const APPLY: u8 = 0xc3;
const SET: u8 = 0xc4;
const SET: u8 = 0xc3;
const APPLY: u8 = 0xc4;
// Used to turn the panel on and off
// The next byte can be 0x03 for "on" and 0x00 for "off"
const ON_OFF: u8 = 0x04;
use crate::config::Config;
use asus_nb::error::AuraError;
use log::{error, info, warn};
use rusb::{Device, DeviceHandle};
use std::convert::TryInto;
use std::error::Error;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::Receiver;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use zbus::dbus_interface;
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())
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum AnimatrixCommand {
Apply,
Set,
SetBoot(bool),
Write(Vec<u8>),
WriteImage(Vec<Vec<u8>>),
//ReloadLast,
}
@@ -34,32 +50,64 @@ pub struct CtrlAnimeDisplay {
initialised: bool,
}
use ::dbus::{nonblock::SyncConnection, tree::Signal};
use async_trait::async_trait;
//AnimatrixWrite
pub trait Dbus {
fn set_anime(&mut self, input: Vec<Vec<u8>>);
#[async_trait]
impl crate::Controller for CtrlAnimeDisplay {
type A = Vec<Vec<u8>>;
fn set_on_off(&mut self, status: bool);
/// Spawns two tasks which continuously check for changes
fn spawn_task_loop(
mut self,
_: Arc<Mutex<Config>>,
mut recv: Receiver<Self::A>,
_: Option<Arc<SyncConnection>>,
_: Option<Arc<Signal<()>>>,
) -> Vec<JoinHandle<()>> {
vec![tokio::spawn(async move {
while let Some(image) = recv.recv().await {
self.do_command(AnimatrixCommand::WriteImage(image))
.await
.unwrap_or_else(|err| warn!("{}", err));
}
})]
fn set_boot_on_off(&mut self, status: bool);
}
impl crate::ZbusAdd for CtrlAnimeDisplay {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Anime".try_into().unwrap(), self)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err
})
.ok();
}
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl Dbus for CtrlAnimeDisplay {
fn set_anime(&mut self, input: Vec<Vec<u8>>) {
self.do_command(AnimatrixCommand::WriteImage(input))
.map_or_else(|err| warn!("{}", err), |()| info!("Writing image to Anime"));
}
async fn reload_from_config(&mut self, _: &mut Config) -> Result<(), Box<dyn Error>> {
Ok(())
fn set_on_off(&mut self, status: bool) {
let mut flush: Vec<u8> = vec![0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = WRITE;
flush[2] = ON_OFF;
let status_str;
if status {
flush[3] = 0x03;
status_str = "on";
} else {
flush[3] = 0x00;
status_str = "off";
}
self.do_command(AnimatrixCommand::Write(flush)).map_or_else(
|err| warn!("{}", err),
|()| info!("Turning {} the AniMe", status_str),
);
}
fn set_boot_on_off(&mut self, status: bool) {
let status_str = if status { "on" } else { "off" };
self.do_command(AnimatrixCommand::SetBoot(status))
.and_then(|()| self.do_command(AnimatrixCommand::Apply))
.map_or_else(
|err| warn!("{}", err),
|()| info!("Turning {} the AniMe at boot/shutdown", status_str),
);
}
}
@@ -100,15 +148,17 @@ impl CtrlAnimeDisplay {
Err(rusb::Error::NoDevice)
}
pub async fn do_command(&mut self, command: AnimatrixCommand) -> Result<(), AuraError> {
pub fn do_command(&mut self, command: AnimatrixCommand) -> Result<(), AuraError> {
if !self.initialised {
self.do_initialization().await?
self.do_initialization()?
}
match command {
AnimatrixCommand::WriteImage(effect) => self.write_image(effect).await?,
AnimatrixCommand::Set => self.do_set().await?,
AnimatrixCommand::Apply => self.do_apply().await?,
AnimatrixCommand::Apply => self.do_apply()?,
//AnimatrixCommand::Set => self.do_set_boot()?,
AnimatrixCommand::SetBoot(status) => self.do_set_boot(status)?,
AnimatrixCommand::Write(bytes) => self.write_bytes(&bytes)?,
AnimatrixCommand::WriteImage(effect) => self.write_image(effect)?,
//AnimatrixCommand::ReloadLast => self.reload_last_builtin(&config).await?,
}
Ok(())
@@ -116,7 +166,7 @@ impl CtrlAnimeDisplay {
/// Should only be used if the bytes you are writing are verified correct
#[inline]
async fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
match self.handle.write_control(
0x21, // request_type
0x09, // request
@@ -150,22 +200,22 @@ impl CtrlAnimeDisplay {
///
/// Where led brightness is 0..255, low to high
#[inline]
async fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
for row in image.iter() {
self.write_bytes(row).await?;
self.write_bytes(row)?;
}
self.do_flush().await?;
self.do_flush()?;
Ok(())
}
#[inline]
async fn do_initialization(&mut self) -> Result<(), AuraError> {
fn do_initialization(&mut self) -> Result<(), AuraError> {
let mut init = [0; PACKET_SIZE];
init[0] = DEV_PAGE; // This is the USB page we're using throughout
for (idx, byte) in INIT_STR.as_bytes().iter().enumerate() {
init[idx + 1] = *byte
}
self.write_bytes(&init).await?;
self.write_bytes(&init)?;
// clear the init array and write other init message
for ch in init.iter_mut() {
@@ -174,43 +224,43 @@ impl CtrlAnimeDisplay {
init[0] = DEV_PAGE; // write it to be sure?
init[1] = INIT;
self.write_bytes(&init).await?;
self.write_bytes(&init)?;
self.initialised = true;
Ok(())
}
#[inline]
async fn do_flush(&mut self) -> Result<(), AuraError> {
fn do_flush(&mut self) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = WRITE;
flush[2] = 0x03;
self.write_bytes(&flush).await?;
self.write_bytes(&flush)?;
Ok(())
}
#[inline]
async fn do_set(&mut self) -> Result<(), AuraError> {
fn do_set_boot(&mut self, status: bool) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = SET;
flush[2] = 0x01;
flush[3] = 0x80;
flush[3] = if status { 0x00 } else { 0x80 };
self.write_bytes(&flush).await?;
self.write_bytes(&flush)?;
Ok(())
}
#[inline]
async fn do_apply(&mut self) -> Result<(), AuraError> {
fn do_apply(&mut self) -> Result<(), AuraError> {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = APPLY;
flush[2] = 0x01;
flush[3] = 0x80;
self.write_bytes(&flush).await?;
self.write_bytes(&flush)?;
Ok(())
}
}

View File

@@ -1,74 +1,106 @@
use crate::config::Config;
use log::{error, info, warn};
use std::error::Error;
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 tokio::sync::mpsc::Receiver;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use std::sync::Mutex;
use zbus::dbus_interface;
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
pub struct CtrlCharge {
path: &'static str,
#[derive(Serialize, Deserialize)]
pub struct ChargeSupportedFunctions {
pub charge_level_set: bool,
}
use ::dbus::{nonblock::SyncConnection, tree::Signal};
use async_trait::async_trait;
impl GetSupported for CtrlCharge {
type A = ChargeSupportedFunctions;
#[async_trait]
impl crate::Controller for CtrlCharge {
type A = u8;
fn get_supported() -> Self::A {
ChargeSupportedFunctions {
charge_level_set: CtrlCharge::get_battery_path().is_ok(),
}
}
}
/// 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));
}
})]
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();
}
}
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
config.read();
info!("Reloaded battery charge limit");
self.set_charge_limit(config.bat_charge_limit, config)
pub fn limit(&self) -> i8 {
if let Ok(config) = self.config.try_lock() {
return config.bat_charge_limit as i8;
}
-1
}
#[dbus_interface(signal)]
pub fn notify_charge(&self, limit: u8) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for CtrlCharge {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Charge".try_into().unwrap(), self)
.map_err(|err| {
warn!("CtrlCharge: add_to_server {}", err);
err
})
.ok();
}
}
impl crate::Reloadable for CtrlCharge {
fn reload(&mut self) -> Result<(), RogError> {
if let Ok(mut config) = self.config.try_lock() {
config.read();
self.set(config.bat_charge_limit, &mut config)?;
}
Ok(())
}
}
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 })
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
CtrlCharge::get_battery_path()?;
Ok(CtrlCharge { config })
}
fn get_battery_path() -> Result<&'static str, std::io::Error> {
fn get_battery_path() -> Result<&'static str, RogError> {
if Path::new(BAT_CHARGE_PATH).exists() {
Ok(BAT_CHARGE_PATH)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Charge control not available",
Err(RogError::MissingFunction(
"Charge control not available, you may require a v5.8.10 series kernel or newer"
.into(),
))
}
}
pub(super) fn set_charge_limit(
&self,
limit: u8,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> {
if limit < 20 || limit > 100 {
warn!(
"Unable to set battery charge limit, must be between 20-100: requested {}",
@@ -78,13 +110,10 @@ impl CtrlCharge {
let mut file = OpenOptions::new()
.write(true)
.open(self.path)
.map_err(|err| {
warn!("Failed to open battery charge limit path: {}", err);
err
})?;
.open(BAT_CHARGE_PATH)
.map_err(|err| RogError::Path(BAT_CHARGE_PATH.into(), err))?;
file.write_all(limit.to_string().as_bytes())
.unwrap_or_else(|err| error!("Could not write to {}, {}", BAT_CHARGE_PATH, err));
.map_err(|err| RogError::Write(BAT_CHARGE_PATH.into(), err))?;
info!("Battery charge limit: {}", limit);
config.read();

View File

@@ -1,149 +1,257 @@
use crate::config::Config;
use crate::config::Profile;
use crate::{
config::{Config, Profile},
GetSupported,
};
use asus_nb::profile::ProfileEvent;
use log::{error, info, warn};
use std::error::Error;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::mpsc::Receiver;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use std::sync::Mutex;
use zbus::dbus_interface;
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
pub struct CtrlFanAndCPU {
path: &'static str,
pub path: &'static str,
config: Arc<Mutex<Config>>,
}
use ::dbus::{nonblock::SyncConnection, tree::Signal};
use async_trait::async_trait;
#[derive(Serialize, Deserialize)]
pub struct FanCpuSupportedFunctions {
pub stock_fan_modes: bool,
pub min_max_freq: bool,
pub fan_curve_set: bool,
}
#[async_trait]
impl crate::Controller for CtrlFanAndCPU {
type A = ProfileEvent;
impl GetSupported for CtrlFanAndCPU {
type A = FanCpuSupportedFunctions;
/// 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(event) = recv.recv().await {
let mut config = config1.lock().await;
let mut lock = gate1.lock().await;
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(),
}
}
}
config.read();
lock.handle_profile_event(&event, &mut config)
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_else(|_| ());
}
}),
// need to watch file path
tokio::spawn(async move {
loop {
tokio::time::delay_for(std::time::Duration::from_millis(100)).await;
let mut lock = gate2.lock().await;
let mut config = config.lock().await;
lock.fan_mode_check_change(&mut config)
.unwrap_or_else(|err| warn!("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));
let profile = config.active_profile.clone();
self.set_profile(&profile, config)?;
info!(
"Reloaded fan mode: {:?}",
FanLevel::from(config.power_profile)
);
/// Fetch the active profile name
fn next_profile(&mut self) {
if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
ctrl.do_next_profile(&mut cfg)
.unwrap_or_else(|err| warn!("{}", err));
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
if let Ok(json) = serde_json::to_string(profile) {
self.notify_profile(&json)
.unwrap_or_else(|err| warn!("{}", err));
}
}
}
}
}
/// Fetch the active profile name
fn active_profile_name(&mut self) -> String {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
return cfg.active_profile.clone();
}
}
"Failed".to_string()
}
/// Fetch the active profile details
fn profile(&mut self) -> String {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
if let Ok(json) = serde_json::to_string(profile) {
return json;
}
}
}
}
"Failed".to_string()
}
fn profiles(&mut self) -> String {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.try_lock() {
cfg.read();
if let Ok(json) = serde_json::to_string(&cfg.power_profiles) {
return json;
}
}
}
"Failed".to_string()
}
#[dbus_interface(signal)]
fn notify_profile(&self, profile: &str) -> zbus::Result<()> {}
}
impl crate::ZbusAdd for DbusFanAndCpu {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Profile".try_into().unwrap(), self)
.map_err(|err| {
warn!("DbusFanAndCpu: 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 mut file = OpenOptions::new()
.write(true)
.open(self.path)
.map_err(|err| RogError::Path(self.path.into(), err))?;
file.write_all(format!("{}\n", config.power_profile).as_bytes())
.map_err(|err| RogError::Write(self.path.into(), err))?;
let profile = config.active_profile.clone();
self.set(&profile, &mut config)?;
info!(
"Reloaded fan mode: {:?}",
FanLevel::from(config.power_profile)
);
}
Ok(())
}
}
impl crate::CtrlTask for CtrlFanAndCPU {
fn do_task(&mut self) -> Result<(), RogError> {
let mut file = OpenOptions::new()
.read(true)
.open(self.path)
.map_err(|err| RogError::Path(self.path.into(), err))?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)
.map_err(|err| RogError::Read(self.path.into(), err))?;
if let Some(num) = char::from(buf[0]).to_digit(10) {
if let Ok(mut config) = self.config.clone().try_lock() {
if config.power_profile != num as u8 {
config.read();
let mut i = config
.toggle_profiles
.iter()
.position(|x| x == &config.active_profile)
.map(|i| i + 1)
.unwrap_or(0);
if i >= config.toggle_profiles.len() {
i = 0;
}
let new_profile = config
.toggle_profiles
.get(i)
.unwrap_or(&config.active_profile)
.clone();
self.set(&new_profile, &mut config)?;
info!("Profile was changed: {}", &new_profile);
}
}
return Ok(());
}
Err(RogError::DoTask("Fan-level could not be parsed".into()))
}
}
impl CtrlFanAndCPU {
pub fn new() -> Result<Self, Box<dyn Error>> {
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
let path = CtrlFanAndCPU::get_fan_path()?;
info!("Device has thermal throttle control");
Ok(CtrlFanAndCPU { path })
Ok(CtrlFanAndCPU { path, config })
}
fn get_fan_path() -> Result<&'static str, std::io::Error> {
fn get_fan_path() -> Result<&'static str, RogError> {
if Path::new(FAN_TYPE_1_PATH).exists() {
Ok(FAN_TYPE_1_PATH)
} else if Path::new(FAN_TYPE_2_PATH).exists() {
Ok(FAN_TYPE_2_PATH)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Fan mode not available, you may require a v5.8 series kernel or newer",
Err(RogError::MissingFunction(
"Fan mode not available, you may require a v5.8.10 series kernel or newer".into(),
))
}
}
pub(super) fn fan_mode_check_change(
&mut self,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
let mut file = OpenOptions::new().read(true).open(self.path)?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)?;
if let Some(num) = char::from(buf[0]).to_digit(10) {
if config.power_profile != num as u8 {
config.read();
/// 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_profile(&new_profile, config)?;
info!("Profile was changed: {}", &new_profile);
}
return Ok(());
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 err = std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Fan-level could not be parsed",
);
Err(Box::new(err))
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(())
}
pub(super) fn set_fan_mode(
&mut self,
preset: u8,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
pub(super) fn set_fan_mode(&mut self, preset: u8, config: &mut Config) -> Result<(), RogError> {
let mode = config.active_profile.clone();
let mut fan_ctrl = OpenOptions::new().write(true).open(self.path)?;
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
@@ -154,7 +262,7 @@ impl CtrlFanAndCPU {
config.write();
fan_ctrl
.write_all(format!("{}\n", preset).as_bytes())
.unwrap_or_else(|err| error!("Could not write to {}, {}", self.path, err));
.map_err(|err| RogError::Write(self.path.into(), err))?;
info!("Fan mode set to: {:?}", FanLevel::from(preset));
self.set_pstate_for_fan_mode(&mode, config)?;
self.set_fan_curve_for_fan_mode(&mode, config)?;
@@ -165,8 +273,9 @@ impl CtrlFanAndCPU {
&mut self,
event: &ProfileEvent,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
) -> Result<(), RogError> {
match event {
ProfileEvent::Toggle => self.do_next_profile(config)?,
ProfileEvent::ChangeMode(mode) => {
self.set_fan_mode(*mode, config)?;
}
@@ -204,21 +313,24 @@ impl CtrlFanAndCPU {
profile.fan_curve = Some(curve.clone());
}
self.set_profile(&profile_key, config)?;
self.set(&profile_key, config)?;
}
}
Ok(())
}
fn set_profile(&mut self, profile: &str, config: &mut Config) -> Result<(), Box<dyn Error>> {
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)?;
let mut fan_ctrl = OpenOptions::new()
.write(true)
.open(self.path)
.map_err(|err| RogError::Path(self.path.into(), err))?;
fan_ctrl
.write_all(format!("{}\n", mode_config.fan_preset).as_bytes())
.unwrap_or_else(|err| error!("Could not write to {}, {}", self.path, err));
.map_err(|err| RogError::Write(self.path.into(), err))?;
config.power_profile = mode_config.fan_preset;
self.set_pstate_for_fan_mode(profile, config)?;
@@ -230,12 +342,7 @@ impl CtrlFanAndCPU {
Ok(())
}
fn set_pstate_for_fan_mode(
&self,
// mode: FanLevel,
mode: &str,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
fn set_pstate_for_fan_mode(&self, mode: &str, config: &mut Config) -> Result<(), RogError> {
info!("Setting pstate");
let mode_config = config
.power_profiles
@@ -257,25 +364,17 @@ impl CtrlFanAndCPU {
let mut file = OpenOptions::new()
.write(true)
.open(AMD_BOOST_PATH)
.map_err(|err| {
warn!("Failed to open AMD boost: {}", err);
err
})?;
.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())
.unwrap_or_else(|err| error!("Could not write to {}, {}", AMD_BOOST_PATH, err));
.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: FanLevel,
mode: &str,
config: &Config,
) -> Result<(), Box<dyn Error>> {
fn set_fan_curve_for_fan_mode(&self, mode: &str, config: &Config) -> Result<(), RogError> {
let mode_config = &config
.power_profiles
.get(mode)

View File

@@ -0,0 +1,38 @@
use std::error;
use std::fmt;
use crate::error::RogError;
#[derive(Debug)]
pub enum GfxError {
ParseVendor,
Path(String, std::io::Error),
Read(String, std::io::Error),
Write(String, std::io::Error),
Module(String, std::io::Error),
Bus(String, std::io::Error),
Command(String, std::io::Error),
}
impl fmt::Display for GfxError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
GfxError::Module(func, error) => write!(f, "Module error: {}: {}", func, error),
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
}
}
}
impl error::Error for GfxError {}
impl From<GfxError> for RogError {
fn from(err: GfxError) -> Self {
RogError::GfxSwitching(err)
}
}

View File

@@ -0,0 +1,415 @@
use ctrl_gfx::error::GfxError;
use ctrl_gfx::*;
use ctrl_rog_bios::CtrlRogBios;
use log::{error, info, warn};
use std::io::Write;
use std::iter::FromIterator;
use std::path::Path;
use std::process::Command;
use std::str::FromStr;
use std::{sync::Arc, sync::Mutex};
use sysfs_class::{PciDevice, SysClass};
use system::{GraphicsDevice, Module, PciBus};
use vendors::{GfxCtrlAction, GfxVendors};
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>,
initfs_cmd: Option<Command>,
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_vendor().unwrap_or_else(|err| format!("Get vendor failed: {}", err))
}
fn power(&self) -> String {
Self::get_runtime_status().unwrap_or_else(|err| format!("Get power status failed: {}", err))
}
fn set_vendor(&mut self, vendor: String) {
if let Ok(tmp) = GfxVendors::from_str(&vendor) {
let action = self.set(tmp).unwrap_or_else(|err| {
warn!("{}", err);
format!("Failed: {}", err.to_string())
});
self.notify_gfx(&vendor)
.unwrap_or_else(|err| warn!("{}", err));
self.notify_action(&action)
.unwrap_or_else(|err| warn!("{}", err));
}
}
#[dbus_interface(signal)]
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()> {}
#[dbus_interface(signal)]
fn notify_action(&self, action: &str) -> zbus::Result<()> {}
}
impl 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: {:?}", CtrlGraphics::get_vendor()?);
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)));
}
}
}
}
let mut initfs_cmd = None;
if Path::new(INITRAMFS_PATH).exists() {
let mut cmd = Command::new("update-initramfs");
cmd.arg("-u");
initfs_cmd = Some(cmd);
info!("Using initramfs update command 'update-initramfs'");
} else if Path::new(DRACUT_PATH).exists() {
let mut cmd = Command::new("dracut");
cmd.arg("-f");
initfs_cmd = Some(cmd);
info!("Using initramfs update command 'dracut'");
}
Ok(CtrlGraphics {
bus,
_amd: amd,
_intel: intel,
nvidia,
other,
initfs_cmd,
config,
})
}
fn get_prime_discrete() -> Result<String, RogError> {
let s = std::fs::read_to_string(PRIME_DISCRETE_PATH)
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?
.trim()
.to_owned();
Ok(s)
}
fn set_prime_discrete(mode: &str) -> Result<(), RogError> {
std::fs::write(PRIME_DISCRETE_PATH, mode)
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?;
Ok(())
}
/// Associated method to get which vendor mode is set
pub fn get_vendor() -> Result<String, RogError> {
let mode = match Self::get_prime_discrete() {
Ok(m) => m,
Err(_) => "nvidia".to_string(),
};
let modules = Module::all().map_err(|err| GfxError::Read("get_vendor".into(), err))?;
let driver_loaded = if modules
.iter()
.any(|module| module.name == "nouveau" || module.name == "nvidia")
{
true
} else {
false
};
let vendor = if mode == "off" {
if driver_loaded {
info!("dGPU driver loaded for compute mode");
"compute".to_string()
} else {
info!("No dGPU driver loaded");
"integrated".to_string()
}
} else {
info!("Assuming dGPU driver loaded");
if mode == "on-demand" {
"hybrid".to_string()
} else {
"nvidia".to_string()
}
};
Ok(vendor)
}
fn is_switching_prime_modes(&self, vendor: &GfxVendors) -> Result<bool, RogError> {
let prev_mode = GfxVendors::from_str(&Self::get_vendor()?)?;
if prev_mode == GfxVendors::Integrated
&& (*vendor == GfxVendors::Hybrid || *vendor == GfxVendors::Nvidia)
{
return Ok(true);
}
if (prev_mode == GfxVendors::Hybrid || prev_mode == GfxVendors::Nvidia)
&& *vendor == GfxVendors::Integrated
{
return Ok(true);
}
if let Ok(config) = self.config.clone().try_lock() {
if CtrlRogBios::has_dedicated_gfx_toggle() && config.gfx_nv_mode_is_dedicated {
if prev_mode == GfxVendors::Hybrid && *vendor == GfxVendors::Nvidia {
return Ok(true);
}
if *vendor == GfxVendors::Hybrid && prev_mode == GfxVendors::Nvidia {
return Ok(true);
}
}
}
Ok(false)
}
pub fn set_gfx_config(vendor: GfxVendors) -> Result<(), RogError> {
let mode = if vendor == GfxVendors::Hybrid {
"on-demand\n"
} else if vendor == GfxVendors::Nvidia {
"on\n"
} else {
// Integrated or Compute
"off\n"
};
info!("Setting {} to {}", PRIME_DISCRETE_PATH, mode);
Self::set_prime_discrete(mode)?;
{
info!("Writing {}", MODPROBE_PATH);
let mut file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(MODPROBE_PATH)
.map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;
let text = if vendor == GfxVendors::Hybrid {
MODPROBE_HYBRID
} else if vendor == GfxVendors::Compute {
MODPROBE_COMPUTE
} else if vendor == GfxVendors::Nvidia {
MODPROBE_NVIDIA
} else {
MODPROBE_INTEGRATED
};
file.write_all(text)
.and_then(|_| file.sync_all())
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
}
info!("Writing {}", PRIMARY_GPU_XORG_PATH);
// begin section for non-separated Nvidia xorg modules
// eg, not put in their own directory
let mut file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(PRIMARY_GPU_XORG_PATH)
.map_err(|err| GfxError::Write(PRIMARY_GPU_XORG_PATH.into(), err))?;
let text = if vendor == GfxVendors::Nvidia {
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
} else {
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
};
file.write_all(&text)
.and_then(|_| file.sync_all())
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
let action = if vendor == GfxVendors::Nvidia {
info!("Enabling nvidia-fallback.service");
"enable"
} else {
info!("Disabling nvidia-fallback.service");
"disable"
};
let status = Command::new("systemctl")
.arg(action)
.arg("nvidia-fallback.service")
.status()
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
if !status.success() {
// Error is ignored in case this service is removed
warn!(
"systemctl: {} (ignore warning if service does not exist!)",
status
);
}
Ok(())
}
/// Write out config files if required, enable/disable relevant services, and update the ramdisk
fn set(&mut self, vendor: GfxVendors) -> Result<String, RogError> {
// Switching from hybrid to/from nvidia shouldn't require a ramdisk update
// or a reboot.
let reboot = self.is_switching_prime_modes(&vendor)?;
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));
}
}
}
}
Self::set_gfx_config(vendor.clone())?;
let mut required_action = GfxCtrlAction::None;
if reboot {
info!("Updating initramfs");
if let Some(cmd) = self.initfs_cmd.as_mut() {
// If switching to Nvidia dedicated we need these modules included
if Path::new(DRACUT_PATH).exists() && vendor == GfxVendors::Nvidia {
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");
}
let status = cmd
.status()
.map_err(|err| GfxError::Write(format!("{:?}", cmd), err))?;
if !status.success() {
error!("Ram disk update failed");
return Ok("Ram disk update failed".into());
} else {
info!("Successfully updated iniramfs");
}
}
required_action = GfxCtrlAction::Reboot;
} else if !reboot {
required_action = GfxCtrlAction::RestartX;
}
Ok(required_action.into())
}
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| GfxError::Read(PATH.into(), err))?;
Ok(buf)
}
fn set_power(&self, power: bool) -> Result<(), RogError> {
if power {
info!("Enabling graphics power");
self.bus
.rescan()
.map_err(|err| GfxError::Bus("bus rescan error".into(), err))?;
} else {
info!("Disabling graphics power");
// Unbind NVIDIA graphics devices and their functions
let unbinds = self.nvidia.iter().map(|dev| dev.unbind());
// Remove NVIDIA graphics devices and their functions
let removes = self.nvidia.iter().map(|dev| dev.remove());
Result::from_iter(unbinds.chain(removes))
.map_err(|err| GfxError::Command("device unbind error".into(), err))?;
}
Ok(())
}
fn auto_power(&self) -> Result<(), RogError> {
let vendor = CtrlGraphics::get_vendor()?;
self.set_power(vendor != "integrated")
}
}

View File

@@ -0,0 +1,57 @@
pub mod vendors;
pub mod error;
pub mod gfx;
pub mod system;
const PRIME_DISCRETE_PATH: &str = "/etc/prime-discrete";
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
const DRACUT_PATH: &str = "/usr/bin/dracut";
static MODPROBE_NVIDIA: &[u8] = MODPROBE_HYBRID;
static MODPROBE_HYBRID: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
alias i2c_nvidia_gpu off
options nvidia NVreg_DynamicPowerManagement=0x02
options nvidia-drm modeset=1
"#;
static MODPROBE_COMPUTE: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
alias i2c_nvidia_gpu off
options nvidia NVreg_DynamicPowerManagement=0x02
options nvidia-drm modeset=0
"#;
static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd
blacklist i2c_nvidia_gpu
blacklist nouveau
blacklist nvidia
blacklist nvidia-drm
blacklist nvidia-modeset
alias i2c_nvidia_gpu off
alias nouveau off
alias nvidia off
alias nvidia-drm off
alias nvidia-modeset off
"#;
const PRIMARY_GPU_XORG_PATH: &str = "/etc/X11/xorg.conf.d/90-nvidia-primary.conf";
static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd
Section "OutputClass"
Identifier "nvidia"
MatchDriver "nvidia-drm"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration"
Option "AllowExternalGpus""#;
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
Option "PrimaryGPU" "true""#;
static PRIMARY_GPU_END: &[u8] = br#"
EndSection"#;

View File

@@ -0,0 +1,127 @@
use log::{error, info, warn};
use std::fs::read_to_string;
use std::{fs::write, io, path::PathBuf};
use sysfs_class::{PciDevice, SysClass};
pub struct Module {
pub name: String,
}
impl Module {
fn parse(line: &str) -> io::Result<Module> {
let mut parts = line.split(' ');
let name = parts
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
Ok(Module {
name: name.to_string(),
})
}
pub fn all() -> io::Result<Vec<Module>> {
let mut modules = Vec::new();
let data = read_to_string("/proc/modules")?;
for line in data.lines() {
let module = Module::parse(line)?;
modules.push(module);
}
Ok(modules)
}
}
pub struct PciBus {
path: PathBuf,
}
impl PciBus {
pub fn new() -> io::Result<PciBus> {
let path = PathBuf::from("/sys/bus/pci");
if path.is_dir() {
Ok(PciBus { path })
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"pci directory not found",
))
}
}
pub fn rescan(&self) -> io::Result<()> {
write(self.path.join("rescan"), "1")
}
}
pub struct GraphicsDevice {
id: String,
functions: Vec<PciDevice>,
}
impl GraphicsDevice {
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
GraphicsDevice { id, functions }
}
pub fn exists(&self) -> bool {
self.functions.iter().any(|func| func.path().exists())
}
pub fn unbind(&self) -> Result<(), std::io::Error> {
for func in self.functions.iter() {
if func.path().exists() {
match func.driver() {
Ok(driver) => {
info!("{}: Unbinding {}", driver.id(), func.id());
unsafe {
driver.unbind(&func).map_err(|err| {
error!("gfx unbind: {}", err);
err
})?;
}
}
Err(err) => match err.kind() {
io::ErrorKind::NotFound => (),
_ => {
error!("gfx driver: {:?}, {}", func.path(), err);
return Err(err);
}
},
}
}
}
Ok(())
}
pub fn remove(&self) -> Result<(), std::io::Error> {
for func in self.functions.iter() {
if func.path().exists() {
match func.driver() {
Ok(driver) => {
error!("{}: in use by {}", func.id(), driver.id());
}
Err(why) => match why.kind() {
std::io::ErrorKind::NotFound => {
info!("{}: Removing", func.id());
unsafe {
// ignore errors and carry on
if let Err(err) = func.remove() {
error!("gfx remove: {}", err);
}
}
}
_ => {
error!("Remove device failed");
}
},
}
} else {
warn!("{}: Already removed", func.id());
}
}
info!("Removed all gfx devices");
Ok(())
}
}

View File

@@ -0,0 +1,86 @@
#[derive(Debug, PartialEq, Clone)]
pub enum GfxVendors {
Nvidia,
Integrated,
Compute,
Hybrid,
}
use std::str::FromStr;
use super::error::GfxError;
impl FromStr for GfxVendors {
type Err = GfxError;
fn from_str(s: &str) -> Result<Self, GfxError> {
match s.to_lowercase().as_str() {
"nvidia" => Ok(GfxVendors::Nvidia),
"hybrid" => Ok(GfxVendors::Hybrid),
"compute" => Ok(GfxVendors::Compute),
"integrated" => Ok(GfxVendors::Integrated),
"nvidia\n" => Ok(GfxVendors::Nvidia),
"hybrid\n" => Ok(GfxVendors::Hybrid),
"compute\n" => Ok(GfxVendors::Compute),
"integrated\n" => Ok(GfxVendors::Integrated),
_ => Err(GfxError::ParseVendor),
}
}
}
impl From<&GfxVendors> for &str {
fn from(mode: &GfxVendors) -> Self {
match mode {
GfxVendors::Nvidia => "nvidia",
GfxVendors::Hybrid => "hybrid",
GfxVendors::Compute => "compute",
GfxVendors::Integrated => "integrated",
}
}
}
#[derive(Debug)]
pub enum GfxCtrlAction {
Reboot,
RestartX,
None,
}
impl FromStr for GfxCtrlAction {
type Err = GfxError;
fn from_str(s: &str) -> Result<Self, GfxError> {
match s.to_lowercase().as_str() {
"reboot" => Ok(GfxCtrlAction::Reboot),
"restartx" => Ok(GfxCtrlAction::RestartX),
"none" => Ok(GfxCtrlAction::None),
_ => Err(GfxError::ParseVendor),
}
}
}
impl From<&GfxCtrlAction> for &str {
fn from(mode: &GfxCtrlAction) -> Self {
match mode {
GfxCtrlAction::Reboot => "reboot",
GfxCtrlAction::RestartX => "restartx",
GfxCtrlAction::None => "none",
}
}
}
impl From<&GfxCtrlAction> for String {
fn from(mode: &GfxCtrlAction) -> Self {
match mode {
GfxCtrlAction::Reboot => "reboot".into(),
GfxCtrlAction::RestartX => "restartx".into(),
GfxCtrlAction::None => "none".into(),
}
}
}
impl From<GfxCtrlAction> for String {
fn from(mode: GfxCtrlAction) -> Self {
(&mode).into()
}
}

View File

@@ -2,152 +2,324 @@
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, laptops::HELP_ADDRESS};
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
use crate::{
config::Config,
error::RogError,
laptops::{match_laptop, HELP_ADDRESS},
};
use asus_nb::{
aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH,
aura_brightness_bytes,
aura_modes::{AuraModes, PER_KEY},
fancy::KeyColourArray,
LED_MSG_LEN,
};
use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal};
use log::{info, warn};
use std::error::Error;
use log::{error, info, warn};
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::sync::Arc;
use tokio::sync::mpsc::Receiver;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use std::sync::Mutex;
use std::{convert::TryInto, path::Path};
use zbus::dbus_interface;
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)
.map(|x| *x)
.collect();
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>,
bright_node: String,
pub bright_node: String,
supported_modes: Vec<u8>,
flip_effect_write: bool,
config: Arc<Mutex<Config>>,
}
use async_trait::async_trait;
pub struct DbusKbdBacklight {
inner: Arc<Mutex<CtrlKbdBacklight>>,
}
#[async_trait]
impl crate::Controller for CtrlKbdBacklight {
type A = AuraModes;
impl DbusKbdBacklight {
pub fn new(inner: Arc<Mutex<CtrlKbdBacklight>>) -> Self {
Self { inner }
}
}
/// Spawns two tasks which continuously check for changes
fn spawn_task_loop(
self,
config: Arc<Mutex<Config>>,
mut recv: Receiver<Self::A>,
connection: Option<Arc<SyncConnection>>,
signal: Option<Arc<Signal<()>>>,
) -> Vec<JoinHandle<()>> {
let gate1 = Arc::new(Mutex::new(self));
let gate2 = gate1.clone();
trait Dbus {
fn set_led(&mut self, data: String);
fn ledmode(&self) -> String;
fn notify_led(&self, data: &str) -> zbus::Result<()>;
}
let config1 = config.clone();
vec![
tokio::spawn(async move {
while let Some(command) = recv.recv().await {
let mut config = config1.lock().await;
let mut lock = gate1.lock().await;
match &command {
impl crate::ZbusAdd for DbusKbdBacklight {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Led".try_into().unwrap(), self)
.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(_) => {
lock.do_command(command, &mut config)
.await
ctrl.do_command(data, &mut cfg)
.unwrap_or_else(|err| warn!("{}", err));
}
_ => {
let json = serde_json::to_string(&command).unwrap();
lock.do_command(command, &mut config)
.await
.unwrap_or_else(|err| warn!("{}", err));
connection
.as_ref()
.expect("LED Controller must have DBUS connection")
.send(
signal
.as_ref()
.expect("LED Controller must have DBUS signal")
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
.append1(json),
)
.unwrap_or_else(|_| 0);
if let Ok(json) = serde_json::to_string(&data) {
ctrl.do_command(data, &mut cfg)
.unwrap_or_else(|err| warn!("{}", err));
self.notify_led(&json).ok();
}
}
}
}
}),
tokio::spawn(async move {
loop {
tokio::time::delay_for(std::time::Duration::from_millis(100)).await;
let mut lock = gate2.lock().await;
let mut config = config.lock().await;
lock.let_bright_check_change(&mut config)
.unwrap_or_else(|err| warn!("led_ctrl: {}", err));
}
}),
]
}
} else {
warn!("SetKeyBacklight could not deserialise");
}
}
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
// set current mode (if any)
if self.supported_modes.len() > 1 {
if self.supported_modes.contains(&config.kbd_backlight_mode) {
let mode = config
.get_led_mode_data(config.kbd_backlight_mode)
.ok_or(RogError::NotSupported)?
.to_owned();
self.write_mode(&mode).await?;
info!("Reloaded last used mode");
} else {
warn!(
"An unsupported mode was set: {}, reset to first mode available",
<&str>::from(&<AuraModes>::from(config.kbd_backlight_mode))
);
for (idx, mode) in config.kbd_backlight_modes.iter_mut().enumerate() {
if !self.supported_modes.contains(&mode.into()) {
config.kbd_backlight_modes.remove(idx);
config.write();
break;
fn next_led_mode(&self) {
if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
ctrl.toggle_mode(false, &mut cfg)
.unwrap_or_else(|err| warn!("{}", err));
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
if let Ok(json) = serde_json::to_string(&mode) {
self.notify_led(&json)
.unwrap_or_else(|err| warn!("{}", err));
}
}
config.kbd_backlight_mode = self.supported_modes[0];
// TODO: do a recursive call with a boxed dyn future later
let mode = config
.get_led_mode_data(config.kbd_backlight_mode)
.ok_or(RogError::NotSupported)?
.to_owned();
self.write_mode(&mode).await?;
info!("Reloaded last used mode");
}
}
}
// Reload brightness
let bright = config.kbd_led_brightness;
let bytes = aura_brightness_bytes(bright);
self.write_bytes(&bytes).await?;
info!("Reloaded last used brightness");
fn prev_led_mode(&self) {
if let Ok(mut ctrl) = self.inner.try_lock() {
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
ctrl.toggle_mode(true, &mut cfg)
.unwrap_or_else(|err| warn!("{}", err));
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
if let Ok(json) = serde_json::to_string(&mode) {
self.notify_led(&json)
.unwrap_or_else(|err| warn!("{}", err));
}
}
}
}
}
/// Return the current mode data
fn led_mode(&self) -> String {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() {
if let Some(mode) = cfg.get_led_mode_data(cfg.kbd_backlight_mode) {
if let Ok(json) = serde_json::to_string(&mode) {
return json;
}
}
}
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not deserialise".to_string()
}
/// Return a list of available modes
fn led_modes(&self) -> String {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() {
if let Ok(json) = serde_json::to_string(&cfg.kbd_backlight_modes) {
return json;
}
}
}
warn!("SetKeyBacklight could not deserialise");
"SetKeyBacklight could not deserialise".to_string()
}
/// Return the current LED brightness
fn led_brightness(&self) -> i8 {
if let Ok(ctrl) = self.inner.try_lock() {
if let Ok(cfg) = ctrl.config.clone().try_lock() {
return cfg.kbd_led_brightness as i8;
}
}
warn!("SetKeyBacklight could not deserialise");
-1
}
#[dbus_interface(signal)]
fn notify_led(&self, data: &str) -> zbus::Result<()>;
}
impl crate::Reloadable for CtrlKbdBacklight {
fn reload(&mut self) -> Result<(), RogError> {
// set current mode (if any)
if let Ok(mut config) = self.config.clone().try_lock() {
if self.supported_modes.len() > 1 {
if self.supported_modes.contains(&config.kbd_backlight_mode) {
let mode = config
.get_led_mode_data(config.kbd_backlight_mode)
.ok_or(RogError::NotSupported)?
.to_owned();
self.write_mode(&mode)?;
info!("Reloaded last used mode");
} else {
warn!(
"An unsupported mode was set: {}, reset to first mode available",
<&str>::from(&<AuraModes>::from(config.kbd_backlight_mode))
);
for (idx, mode) in config.kbd_backlight_modes.iter_mut().enumerate() {
if !self.supported_modes.contains(&mode.into()) {
config.kbd_backlight_modes.remove(idx);
config.write();
break;
}
}
config.kbd_backlight_mode = self.supported_modes[0];
// TODO: do a recursive call with a boxed dyn future later
let mode = config
.get_led_mode_data(config.kbd_backlight_mode)
.ok_or(RogError::NotSupported)?
.to_owned();
self.write_mode(&mode)?;
info!("Reloaded last used mode");
}
}
// Reload brightness
let bright = config.kbd_led_brightness;
let bytes = aura_brightness_bytes(bright);
self.write_bytes(&bytes)?;
info!("Reloaded last used brightness");
}
Ok(())
}
}
impl crate::CtrlTask for CtrlKbdBacklight {
fn do_task(&mut self) -> Result<(), RogError> {
let mut file = OpenOptions::new()
.read(true)
.open(&self.bright_node)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => {
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
}
_ => RogError::Path((&self.bright_node).into(), err),
})?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)
.map_err(|err| RogError::Read("buffer".into(), err))?;
if let Some(num) = char::from(buf[0]).to_digit(10) {
if let Ok(mut config) = self.config.clone().try_lock() {
if config.kbd_led_brightness != num as u8 {
config.read();
config.kbd_led_brightness = num as u8;
config.write();
}
}
return Ok(());
}
Err(RogError::ParseLED)
}
}
impl CtrlKbdBacklight {
#[inline]
pub fn new(id_product: &str, condev_iface: Option<&String>, supported_modes: Vec<u8>) -> Self {
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
CtrlKbdBacklight {
led_node: Self::get_node_failover(id_product, None, Self::scan_led_node).ok(),
kbd_node: Self::get_node_failover(id_product, condev_iface, Self::scan_kbd_node).ok(),
let ctrl = CtrlKbdBacklight {
// Using `ok` here so we can continue without keyboard features but
// still get brightness control at least... maybe...
led_node: Some(Self::get_node_failover(
id_product,
None,
Self::scan_led_node,
)?),
kbd_node: Some(Self::get_node_failover(
id_product,
condev_iface,
Self::scan_kbd_node,
)?),
// TODO: Check for existance
bright_node: "/sys/class/leds/asus::kbd_backlight/brightness".to_string(),
bright_node: Self::get_kbd_bright_path()?.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, std::io::Error>,
) -> Result<String, std::io::Error> {
fun: fn(&str, Option<&String>) -> Result<String, RogError>,
) -> Result<String, RogError> {
// We do three tries here just to be certain that we avoid systemd unit
// load order issues
for n in 0..=2 {
// 0,1,2 inclusive
match fun(id_product, iface) {
@@ -157,36 +329,41 @@ impl CtrlKbdBacklight {
warn!("Looking for node: {}", e.to_string());
std::thread::sleep(std::time::Duration::from_secs(1));
} else {
return Err(e);
break;
}
}
}
}
// Shouldn't be possible to reach this...
let err = std::io::Error::new(std::io::ErrorKind::NotFound, "node not found");
Err(err)
Err(RogError::NotFound(format!("{}, {:?}", id_product, iface)))
}
fn scan_led_node(id_product: &str, _: Option<&String>) -> Result<String, std::io::Error> {
fn scan_led_node(id_product: &str, _: Option<&String>) -> Result<String, RogError> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
err
RogError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("hidraw").map_err(|err| {
warn!("{}", err);
err
RogError::Udev("match_subsystem failed".into(), err)
})?;
for device in enumerator.scan_devices()? {
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);
err
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
})?
{
if parent.attribute_value("idProduct").unwrap() == id_product {
// && device.parent().unwrap().sysnum().unwrap() == 3
if parent
.attribute_value("idProduct")
.ok_or(RogError::NotFound("LED idProduct".into()))?
== id_product
{
if let Some(dev_node) = device.devnode() {
info!("Using device at: {:?} for LED control", dev_node);
return Ok(dev_node.to_string_lossy().to_string());
@@ -194,31 +371,39 @@ impl CtrlKbdBacklight {
}
}
}
let err = std::io::Error::new(
std::io::ErrorKind::NotFound,
"ASUS LED device node not found",
);
warn!("Did not find a hidraw node for LED control, your device may be unsupported or require a kernel patch, see: {}", HELP_ADDRESS);
Err(err)
Err(RogError::MissingFunction(
"ASUS LED device node not found".into(),
))
}
fn scan_kbd_node(id_product: &str, iface: Option<&String>) -> Result<String, std::io::Error> {
let mut enumerator = udev::Enumerator::new()?;
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);
err
RogError::Udev("match_subsystem failed".into(), err)
})?;
enumerator
.match_property("ID_MODEL_ID", id_product)
.map_err(|err| {
warn!("{}", err);
err
RogError::Udev("match_property failed".into(), err)
})?;
for device in enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
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 {
@@ -230,63 +415,40 @@ impl CtrlKbdBacklight {
}
}
}
let err = std::io::Error::new(
std::io::ErrorKind::NotFound,
"ASUS keyboard 'Consumer Device' node not found",
);
warn!("Did not find keyboard consumer device node, if expected functions are missing please file an issue at {}", HELP_ADDRESS);
Err(err)
Err(RogError::MissingFunction(
"ASUS keyboard 'Consumer Device' node not found".into(),
))
}
fn let_bright_check_change(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
let mut file = OpenOptions::new().read(true).open(&self.bright_node)?;
let mut buf = [0u8; 1];
file.read_exact(&mut buf)?;
if let Some(num) = char::from(buf[0]).to_digit(10) {
if config.kbd_led_brightness != num as u8 {
config.read();
config.kbd_led_brightness = num as u8;
config.write();
}
return Ok(());
}
let err = std::io::Error::new(
std::io::ErrorKind::InvalidData,
"LED brightness could not be parsed",
);
Err(Box::new(err))
}
pub async fn do_command(
&mut self,
mode: AuraModes,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
self.set_and_save(mode, config).await
pub fn do_command(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
self.set_and_save(mode, config)
}
/// Should only be used if the bytes you are writing are verified correct
#[inline]
async fn write_bytes(&self, message: &[u8]) -> Result<(), Box<dyn Error>> {
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) {
file.write_all(message).unwrap();
return Ok(());
return file
.write_all(message)
.map_err(|err| RogError::Write("write_bytes".into(), err));
}
}
Err(Box::new(RogError::NotSupported))
Err(RogError::NotSupported)
}
/// Write an effect block
#[inline]
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), Box<dyn Error>> {
fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
if self.flip_effect_write {
for row in effect.iter().rev() {
self.write_bytes(row).await?;
self.write_bytes(row)?;
}
} else {
for row in effect.iter() {
self.write_bytes(row).await?;
self.write_bytes(row)?;
}
}
self.flip_effect_write = !self.flip_effect_write;
@@ -297,15 +459,11 @@ impl CtrlKbdBacklight {
///
/// This needs to be universal so that settings applied by dbus stick
#[inline]
async fn set_and_save(
&mut self,
mode: AuraModes,
config: &mut Config,
) -> Result<(), Box<dyn Error>> {
fn set_and_save(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
match mode {
AuraModes::LedBrightness(n) => {
let bytes: [u8; LED_MSG_LEN] = (&mode).into();
self.write_bytes(&bytes).await?;
self.write_bytes(&bytes)?;
config.read();
config.kbd_led_brightness = n;
config.write();
@@ -314,15 +472,15 @@ impl CtrlKbdBacklight {
AuraModes::PerKey(v) => {
if v.is_empty() || v[0].is_empty() {
let bytes = KeyColourArray::get_init_msg();
self.write_bytes(&bytes).await?;
self.write_bytes(&bytes)?;
} else {
self.write_effect(&v).await?;
self.write_effect(&v)?;
}
}
_ => {
config.read();
let mode_num: u8 = u8::from(&mode);
self.write_mode(&mode).await?;
self.write_mode(&mode)?;
config.kbd_backlight_mode = mode_num;
config.set_mode_data(mode);
config.write();
@@ -332,14 +490,45 @@ impl CtrlKbdBacklight {
}
#[inline]
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), Box<dyn Error>> {
fn toggle_mode(&mut self, reverse: bool, config: &mut Config) -> Result<(), RogError> {
let current = config.kbd_backlight_mode;
if let Some(idx) = self.supported_modes.iter().position(|v| *v == current) {
let mut idx = idx;
// goes past end of array
if reverse {
if idx == 0 {
idx = self.supported_modes.len() - 1;
} else {
idx -= 1;
}
} else {
idx += 1;
if idx == self.supported_modes.len() {
idx = 0;
}
}
let next = self.supported_modes[idx];
config.read();
if let Some(data) = config.get_led_mode_data(next) {
self.write_mode(&data)?;
config.kbd_backlight_mode = next;
}
config.write();
}
Ok(())
}
#[inline]
fn write_mode(&mut self, mode: &AuraModes) -> Result<(), RogError> {
match mode {
AuraModes::PerKey(v) => {
if v.is_empty() || v[0].is_empty() {
let bytes = KeyColourArray::get_init_msg();
self.write_bytes(&bytes).await?;
self.write_bytes(&bytes)?;
} else {
self.write_effect(v).await?;
self.write_effect(v)?;
}
}
_ => {
@@ -349,20 +538,20 @@ impl CtrlKbdBacklight {
if self.supported_modes.contains(&mode_num) {
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
for array in bytes.iter() {
self.write_bytes(array).await?;
self.write_bytes(array)?;
}
}
}
_ => {
if self.supported_modes.contains(&mode_num) {
let bytes: [u8; LED_MSG_LEN] = mode.into();
self.write_bytes(&bytes).await?;
self.write_bytes(&bytes)?;
}
}
}
self.write_bytes(&LED_SET).await?;
self.write_bytes(&LED_SET)?;
// Changes won't persist unless apply is set
self.write_bytes(&LED_APPLY).await?;
self.write_bytes(&LED_APPLY)?;
}
}
Ok(())

View File

@@ -0,0 +1,261 @@
use crate::{
config::Config,
ctrl_gfx::{gfx::CtrlGraphics, vendors::GfxVendors},
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::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
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 })
}
fn set_path_mutable(path: &str) -> Result<(), RogError> {
use std::process::Command;
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(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))?;
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(())
}
}

View File

@@ -1,27 +1,31 @@
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_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge,
ctrl_fan_cpu::CtrlFanAndCPU, ctrl_leds::CtrlKbdBacklight, dbus::dbus_create_tree,
laptops::match_laptop,
config::Config, laptops::print_board_info, supported::SupportedFunctions, GetSupported,
};
use daemon::{
ctrl_anime::CtrlAnimeDisplay,
ctrl_gfx::{gfx::CtrlGraphics, vendors::GfxVendors},
};
use dbus::{
channel::Sender,
nonblock::{Process, SyncConnection},
tree::Signal,
};
use dbus_tokio::connection;
use asus_nb::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
use daemon::Controller;
use asus_nb::DBUS_NAME;
use daemon::{CtrlTask, Reloadable, ZbusAdd};
use log::LevelFilter;
use log::{error, info, warn};
use std::error::Error;
use std::io::Write;
use std::sync::Arc;
use tokio::sync::Mutex;
use std::sync::Mutex;
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
use 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)
@@ -30,7 +34,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
.init();
info!("Version: {}", daemon::VERSION);
start_daemon().await?;
start_daemon()?;
Ok(())
}
@@ -41,211 +45,147 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
// - 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>> {
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 mut config = if let Some(laptop) = laptop.as_ref() {
Config::default().load(laptop.supported_modes())
let config = if let Some(laptop) = laptop.as_ref() {
Config::load(laptop.supported_modes())
} else {
Config::default().load(&[])
Config::load(&[])
};
let mut led_control = if let Some(laptop) = laptop {
Some(CtrlKbdBacklight::new(
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) = CtrlGraphics::get_vendor() {
if ded == 1 && vendor != "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");
CtrlGraphics::set_gfx_config(GfxVendors::Nvidia)?;
} else if ded == 0 {
info!("Dedicated GFX toggle is off");
}
}
}
} else {
ctrl.reload()
.unwrap_or_else(|err| warn!("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.clone()).add_to_server(&mut object_server);
tasks.push(tmp);
};
if let Some(laptop) = laptop {
if let Ok(ctrl) = CtrlKbdBacklight::new(
laptop.usb_product(),
laptop.condev_iface(),
laptop.supported_modes().to_owned(),
))
} 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,
profile_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(), profile_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);
}
config,
)
.map_err(|err| {
error!("Keyboard control: {}", err);
}) {
let tmp = Arc::new(Mutex::new(ctrl));
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
tasks.push(tmp);
}
});
}
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);
// 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));
connection
.send(
charge_limit_signal
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
.append1(config.bat_charge_limit),
)
.unwrap_or_else(|_| 0);
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();
}
}
});
Ok(())
object_server.with(&"/org/asuslinux/Charge".try_into()?, |obj: &CtrlCharge| {
let x = obj.limit();
obj.notify_charge(x as u8)
})?;
loop {
if let Err(err) = object_server.try_handle_next() {
eprintln!("{}", err);
}
}
}

View File

@@ -1,259 +0,0 @@
use crate::config::Config;
use asus_nb::profile::ProfileEvent;
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")
}
fn set_profile(sender: Sender<ProfileEvent>) -> Method<MTSync, ()> {
let factory = Factory::new_sync::<()>();
factory
// method for profile
.method("ProfileCommand", (), {
move |m| {
let mut iter = m.msg.iter_init();
let byte: String = iter.read()?;
if let Ok(byte) = serde_json::from_str(&byte) {
sender.clone().try_send(byte).unwrap_or_else(|_err| {});
}
Ok(vec![])
}
})
.inarg::<String, _>("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>,
Receiver<ProfileEvent>,
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 (profile_send, profile_recv) = channel::<ProfileEvent>(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_profile(profile_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,
profile_recv,
key_backlight_changed,
fanmode_changed,
chrg_limit_changed,
)
}

View File

@@ -1,21 +1,66 @@
use intel_pstate::PStateError;
use rog_fan_curve::CurveError;
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),
}
impl std::error::Error for RogError {}
impl fmt::Display for RogError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RogError::ParseFanLevel => write!(f, "Parse error"),
RogError::ParseFanLevel => write!(f, "Parse profile error"),
RogError::ParseVendor => write!(f, "Parse gfx vendor error"),
RogError::ParseLED => write!(f, "Parse LED error"),
RogError::MissingProfile(profile) => write!(f, "Profile does not exist {}", profile),
RogError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
RogError::Path(path, error) => write!(f, "Path {}: {}", path, error),
RogError::Read(path, error) => write!(f, "Read {}: {}", path, error),
RogError::Write(path, error) => write!(f, "Write {}: {}", path, error),
RogError::NotSupported => write!(f, "Not supported"),
RogError::NotFound(deets) => write!(f, "Not found: {}", deets),
RogError::IntelPstate(err) => write!(f, "Intel pstate error: {}", err),
RogError::FanCurve(err) => write!(f, "Custom fan-curve error: {}", err),
RogError::DoTask(deets) => write!(f, "Task error: {}", deets),
RogError::MissingFunction(deets) => write!(f, "Missing functionality: {}", deets),
RogError::MissingLedBrightNode(path, error) => write!(f, "Led node at {} is missing, please check you have the required patch or dkms module installed: {}", path, error),
RogError::ReloadFail(deets) => write!(f, "Task error: {}", deets),
RogError::GfxSwitching(deets) => write!(f, "Graphics switching error: {}", deets),
}
}
}
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)
}
}

View File

@@ -27,13 +27,12 @@ impl LaptopBase {
}
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("Failed here").iter() {
let device_desc = device.device_descriptor().expect("Failed there");
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);
return Some(laptop);
}
0x1869 => return Some(select_1866_device("1869".to_owned())),
@@ -61,10 +60,6 @@ fn select_1866_device(prod: 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,
@@ -81,7 +76,18 @@ fn select_1866_device(prod: String) -> LaptopBase {
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 {

View File

@@ -8,38 +8,45 @@ pub mod ctrl_charge;
///
pub mod ctrl_fan_cpu;
///
pub mod ctrl_leds;
pub mod ctrl_gfx;
///
pub mod dbus;
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 supported;
mod error;
use async_trait::async_trait;
use crate::error::RogError;
use config::Config;
use std::error::Error;
use std::sync::Arc;
use tokio::sync::{mpsc::Receiver, Mutex};
use tokio::task::JoinHandle;
use zbus::ObjectServer;
pub static VERSION: &str = "1.1.2";
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
use ::dbus::{nonblock::SyncConnection, tree::Signal};
pub trait Reloadable {
fn reload(&mut self) -> Result<(), RogError>;
}
#[async_trait]
pub trait Controller {
pub trait ZbusAdd {
fn add_to_server(self, server: &mut ObjectServer);
}
pub trait CtrlTask {
fn do_task(&mut self) -> Result<(), RogError>;
}
pub trait CtrlTaskComplex {
type A;
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>>;
/// Spawn an infinitely running task (usually) which checks a Receiver for input,
/// and may send a signal over dbus
fn spawn_task_loop(
self,
config: Arc<Mutex<Config>>,
recv: Receiver<Self::A>,
connection: Option<Arc<SyncConnection>>,
signal: Option<Arc<Signal<()>>>,
) -> Vec<JoinHandle<()>>;
fn do_task(&mut self, config: &mut Config, event: Self::A);
}
pub trait GetSupported {
type A;
fn get_supported() -> Self::A;
}

View File

@@ -1,47 +1,110 @@
use asus_nb::{
cli_options::{LedBrightness, SetAuraBuiltin},
anime_dbus::AniMeDbusWriter,
cli_options::{AniMeActions, AniMeStatusValue, LedBrightness, SetAuraBuiltin},
core_dbus::AuraDbusClient,
profile::{ProfileCommand, ProfileEvent},
};
use daemon::ctrl_fan_cpu::FanLevel;
use gumdrop::Options;
use daemon::{ctrl_fan_cpu::FanLevel, ctrl_gfx::vendors::GfxVendors};
use gumdrop::{Opt, Options};
use log::LevelFilter;
use std::io::Write;
use std::{env::args, io::Write, process::Command};
use yansi_term::Colour::Green;
use yansi_term::Colour::Red;
#[derive(Options)]
#[derive(Default, Options)]
struct CLIStart {
#[options(help = "print help message")]
#[options(help_flag, help = "print help message")]
help: bool,
#[options(help = "show program version number")]
version: bool,
#[options(meta = "VAL", help = "<off, low, med, high>")]
#[options(help = "show supported functions of this laptop")]
show_supported: bool,
#[options(meta = "", 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>")]
#[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<Command>,
command: Option<CliCommand>,
}
#[derive(Options)]
enum Command {
enum CliCommand {
#[options(help = "Set the keyboard lighting from built-in modes")]
LedMode(LedModeCommand),
#[options(help = "Create and configure profiles")]
Profile(ProfileCommand),
#[options(help = "Set the graphics mode")]
Graphics(GraphicsCommand),
#[options(name = "anime", help = "Manage AniMe Matrix")]
AniMe(AniMeCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
#[derive(Options)]
struct LedModeCommand {
#[options(help = "print help message")]
help: bool,
#[options(command, required)]
#[options(help = "switch to next aura mode")]
next_mode: bool,
#[options(help = "switch to previous aura mode")]
prev_mode: bool,
#[options(command)]
command: Option<SetAuraBuiltin>,
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Options)]
struct GraphicsCommand {
#[options(help = "print help message")]
help: bool,
#[options(
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 logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
@@ -49,34 +112,266 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
.filter(None, LevelFilter::Info)
.init();
let parsed = CLIStart::parse_args_default_or_exit();
let mut args: Vec<String> = args().collect();
args.remove(0);
let parsed: CLIStart;
let missing_argument_k = gumdrop::Error::missing_argument(Opt::Short('k'));
match CLIStart::parse_args_default(&args) {
Ok(p) => {
parsed = p;
}
Err(err) if err.to_string() == missing_argument_k.to_string() => {
parsed = CLIStart {
kbd_bright: Some(LedBrightness::new(None)),
..Default::default()
};
}
Err(err) => {
eprintln!("source {}", err);
std::process::exit(2);
}
}
if parsed.help_requested() {
// As help option don't work with `parse_args_default`
// we will call `parse_args_default_or_exit` instead
CLIStart::parse_args_default_or_exit();
}
if parsed.version {
println!("Version: {}", daemon::VERSION);
}
let writer = AuraDbusClient::new()?;
let dbus_client = AuraDbusClient::new()?;
let anime_dbus_client = AniMeDbusWriter::new()?;
match parsed.command {
Some(Command::LedMode(mode)) => {
if let Some(command) = mode.command {
writer.write_builtin_mode(&command.into())?
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);
}
}
if mode.next_mode && mode.prev_mode {
println!("Please specify either next or previous")
}
if mode.next_mode {
dbus_client.next_keyboard_led_mode()?;
} else if mode.prev_mode {
dbus_client.prev_keyboard_led_mode()?;
} else if let Some(command) = mode.command {
dbus_client.write_builtin_mode(&command.into())?
}
}
Some(Command::Profile(command)) => {
writer.write_profile_command(&ProfileEvent::Cli(command))?
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_client.next_fan_profile()?;
} else {
dbus_client.write_profile_command(&ProfileEvent::Cli(cmd))?
}
}
Some(CliCommand::Graphics(cmd)) => do_gfx(cmd, &dbus_client)?,
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 {
anime_dbus_client.turn_on_off(anime_turn.into())?
}
if let Some(anime_boot) = cmd.boot {
anime_dbus_client.turn_boot_on_off(anime_boot.into())?
}
if let Some(action) = cmd.command {
match action {
AniMeActions::Leds(anime_leds) => {
let led_brightness = anime_leds.led_brightness();
anime_dbus_client.set_leds_brightness(led_brightness)?;
}
}
}
}
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_client.set_bios_post_sound(opt)?;
}
if cmd.post_sound_get {
let res = if dbus_client.get_bios_post_sound()? == 1 {
true
} else {
false
};
println!("Bios POST sound on: {}", res);
}
if let Some(opt) = cmd.dedicated_gfx_set {
dbus_client.set_bios_dedicated_gfx(opt)?;
}
if cmd.dedicated_gfx_get {
let res = if dbus_client.get_bios_dedicated_gfx()? == 1 {
true
} else {
false
};
println!("Bios dedicated GPU on: {}", res);
}
}
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());
}
}
None => (),
}
if let Some(brightness) = parsed.kbd_bright {
writer.write_brightness(brightness.level())?;
match brightness.level() {
None => {
let level = dbus_client.get_led_brightness()?;
println!("Current keyboard led brightness: {}", level.to_string());
}
Some(level) => dbus_client.write_brightness(level)?,
}
}
if let Some(fan_level) = parsed.pwr_profile {
writer.write_fan_mode(fan_level.into())?;
if parsed.show_supported {
let dat = dbus_client.get_supported_functions()?;
println!("Supported laptop functions:\n{}", dat.to_string());
}
if let Some(fan_level) = parsed.fan_mode {
dbus_client.write_fan_mode(fan_level.into())?;
}
if let Some(chg_limit) = parsed.chg_limit {
writer.write_charge_limit(chg_limit)?;
dbus_client.write_charge_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!("Updating settings, please wait...");
println!("If this takes longer than 30s, ctrl+c then check `journalctl -b -u asusd`");
dbus_client.write_gfx_mode(<&str>::from(&mode).into())?;
let res = dbus_client.wait_gfx_changed()?;
match res.as_str() {
"reboot" => {
println!(
"{}",
Green.paint("\nGraphics vendor mode changed successfully\n"),
);
do_gfx_action(
command.force,
Command::new("systemctl").arg("reboot").arg("-i"),
"Reboot Linux PC",
"Please reboot when ready",
)?;
}
"restartx" => {
println!(
"{}",
Green.paint("\nGraphics vendor mode changed successfully\n")
);
do_gfx_action(
command.force,
Command::new("systemctl")
.arg("restart")
.arg("display-manager.service"),
"Restart display-manager server",
"Please restart display-manager when ready",
)?;
std::process::exit(1)
}
_ => {
println!("{}", Red.paint(&format!("\n{}\n", res.as_str())),);
std::process::exit(-1);
}
}
std::process::exit(-1)
}
if command.get {
let res = dbus_client.get_gfx_mode()?;
println!("Current graphics mode: {}", res);
}
if command.pow {
let res = dbus_client.get_gfx_pwr()?;
if res.contains("active") {
println!("Current power status: {}", Red.paint(&format!("{}", res)));
} else {
println!("Current power status: {}", Green.paint(&format!("{}", res)));
}
}
Ok(())
}
fn do_gfx_action(
no_confirm: bool,
command: &mut Command,
ask_msg: &str,
cancel_msg: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("{}? y/n", ask_msg);
let mut buf = String::new();
if no_confirm {
let status = command.status()?;
if !status.success() {
println!("systemctl: returned with {}", status);
}
}
std::io::stdin().read_line(&mut buf).expect("Input failed");
let input = buf.chars().next().unwrap() as char;
if input == 'Y' || input == 'y' || no_confirm {
let status = command.status()?;
if !status.success() {
println!("systemctl: returned with {}", status);
}
} else {
println!("{}", Red.paint(&format!("{}", cancel_msg)));
}
Ok(())
}

View 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(),
}
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "asus-nb"
version = "1.1.0"
version = "2.2.1"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
@@ -17,6 +17,8 @@ serde_derive = "^1.0"
serde_json = "^1.0"
yansi-term = "^0.1"
rog_fan_curve = { version = "0.1", features = ["serde"] }
zbus = "^1.8.0"
zvariant = "^2.4.0"
[dev-dependencies]
tinybmp = "^0.2.3"

View File

@@ -14,9 +14,10 @@ fn main() {
let mut matrix = AniMeMatrix::new();
// Aligned left
for px in pixels {
for (i, px) in pixels.iter().enumerate() {
if (px.x as usize / 2) < WIDTH && (px.y as usize) < HEIGHT && px.x % 2 == 0 {
matrix.get_mut()[px.y as usize][px.x as usize / 2] = px.color as u8;
let mut c = px.color as u32;
matrix.get_mut()[px.y as usize][px.x as usize / 2] = c as u8;
}
}

View File

@@ -93,6 +93,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
writer.write_colour_block(&colours)?;
// can change 100 times per second, so need to slow it down
//std::thread::sleep(std::time::Duration::from_millis(30));
std::thread::sleep(std::time::Duration::from_millis(30));
}
}

View File

@@ -1,29 +1,26 @@
use crate::anime_matrix::AniMePacketType;
use crate::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
use dbus::channel::Sender;
use dbus::{blocking::Connection, Message};
use std::error::Error;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::{thread, time::Duration};
const DBUS_ANIME_PATH: &str = "/org/asuslinux/Anime";
pub const ANIME_PANE1_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x01, 0x00, 0x73, 0x02];
pub const ANIME_PANE2_PREFIX: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
use crate::anime_matrix::{AniMeMatrix, AniMePacketType};
use crate::DBUS_NAME;
use dbus::blocking::{Connection, Proxy};
use std::error::Error;
use std::{thread, time::Duration};
use crate::dbus_anime::OrgAsuslinuxDaemon as OrgAsuslinuxDaemonAniMe;
/// Interface for the AniMe dot-matrix display
///
/// The resolution is 34x56 (1904) but only 1,215 LEDs in the top-left are used.
/// The display is available only on select GA401 models.
///
/// Actual image ration when displayed is stretched width.
/// Actual image ratio when displayed is stretched width.
///
/// Data structure should be nested array of [[u8; 33]; 56]
pub struct AniMeDbusWriter {
connection: Box<Connection>,
block_time: u64,
stop: Arc<AtomicBool>,
}
impl AniMeDbusWriter {
@@ -33,46 +30,78 @@ impl AniMeDbusWriter {
Ok(AniMeDbusWriter {
connection: Box::new(connection),
block_time: 25,
stop: Arc::new(AtomicBool::new(false)),
})
}
// Create D-Bus proxy
fn new_proxy(&self) -> Proxy<&Connection> {
self.connection
.with_proxy(DBUS_NAME, DBUS_ANIME_PATH, Duration::from_millis(200))
}
fn thread_sleep(&self) {
thread::sleep(Duration::from_millis(self.block_time));
}
pub fn write_image_to_buf(_buf: &mut AniMePacketType, _image_data: &[u8]) {
unimplemented!("Image format is in progress of being worked out")
}
/// Write an Animatrix image
///
/// The expected input here is *two* Vectors, 640 bytes in length. The two vectors
/// are each one half of the full image write.
/// The expected input here is *two* Vectors, 640 bytes in length.
/// The two vectors are each one half of the full image write.
///
/// After each write a flush is written, it is assumed that this tells the device to
/// go ahead and display the written bytes
/// After each write a flush is written, it is assumed that this tells the
/// device to go ahead and display the written bytes
///
/// # Note:
/// The vectors are expected to contain the full sequence of bytes as follows
/// # Note: The vectors are expected to contain the full sequence of bytes
/// as follows
///
/// - Write packet 1: 0x5e 0xc0 0x02 0x01 0x00 0x73 0x02 .. <led brightness>
/// - Write packet 2: 0x5e 0xc0 0x02 0x74 0x02 0x73 0x02 .. <led brightness>
///
/// Where led brightness is 0..255, low to high
#[inline]
pub fn write_image(&mut self, image: &mut AniMePacketType) -> Result<(), Box<dyn Error>> {
if image[0][0] != ANIME_PANE1_PREFIX[0] && image[0][6] != ANIME_PANE1_PREFIX[6] {
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
}
if image[1][0] != ANIME_PANE2_PREFIX[0] && image[1][6] != ANIME_PANE2_PREFIX[6] {
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
}
pub fn write_image(&self, image: &mut AniMePacketType) -> Result<(), Box<dyn Error>> {
let proxy = self.new_proxy();
image[0][..7].copy_from_slice(&ANIME_PANE1_PREFIX);
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
proxy.set_anime(vec![image[0].to_vec(), image[1].to_vec()])?;
self.thread_sleep();
Ok(())
}
#[inline]
pub fn set_leds_brightness(&self, led_brightness: u8) -> Result<(), Box<dyn Error>> {
let mut anime_matrix = AniMeMatrix::new();
anime_matrix.fill_with(led_brightness);
self.write_image(&mut AniMePacketType::from(anime_matrix))?;
Ok(())
}
#[inline]
pub fn turn_on_off(&self, status: bool) -> Result<(), Box<dyn Error>> {
let proxy = self.new_proxy();
proxy.set_on_off(status)?;
self.thread_sleep();
Ok(())
}
#[inline]
pub fn turn_boot_on_off(&self, status: bool) -> Result<(), Box<dyn Error>> {
let proxy = self.new_proxy();
proxy.set_boot_on_off(status)?;
self.thread_sleep();
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "AnimatrixWrite")?
.append2(image[0].to_vec(), image[1].to_vec());
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
thread::sleep(Duration::from_millis(self.block_time));
if self.stop.load(Ordering::Relaxed) {
panic!("Got signal to stop!");
}
Ok(())
}
}

View File

@@ -46,9 +46,9 @@ impl AniMeMatrix {
// Write the top block of LEDs (first 7 rows)
if count < 6 {
if count % 2 != 0 {
print!(" ");
print!(" ");
} else {
print!(" ");
print!("");
}
let tmp = if count == 0 || count == 1 || count == 3 || count == 5 {
row[1..].iter()
@@ -56,7 +56,7 @@ impl AniMeMatrix {
row.iter()
};
for x in tmp {
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
print!(" {}", RGB(*x, *x, *x).paint("XY"));
}
println!();
@@ -76,13 +76,13 @@ impl AniMeMatrix {
let index = row.len() - prog_row_len;
if count % 2 == 0 {
print!(" ");
print!(" ");
}
for (i, x) in row.iter().enumerate() {
if i >= index {
print!(" {}", RGB(*x, *x, *x).paint(&format!("{:#04X}", x)));
print!(" {}", RGB(*x, *x, *x).paint("XY"));
} else {
print!(" ");
print!(" ");
}
}
println!();

View File

@@ -17,7 +17,7 @@ pub const FLASH: u8 = 0x0c;
pub const MULTISTATIC: u8 = 0x0d;
pub const PER_KEY: u8 = 0xff;
#[derive(Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
impl From<cli_options::Colour> for Colour {
fn from(c: cli_options::Colour) -> Self {
@@ -30,7 +30,7 @@ impl Default for Colour {
}
}
#[derive(Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum Speed {
Low = 0xe1,
Med = 0xeb,
@@ -54,7 +54,7 @@ impl Default for Speed {
/// Used for Rainbow mode.
///
/// Enum corresponds to the required integer value
#[derive(Copy, Clone, Deserialize, Serialize)]
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum Direction {
Right,
Left,
@@ -77,7 +77,7 @@ impl Default for Direction {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct TwoColourSpeed {
pub colour: Colour,
pub colour2: Colour,
@@ -93,7 +93,7 @@ impl From<cli_options::TwoColourSpeed> for TwoColourSpeed {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SingleSpeed {
pub speed: Speed,
}
@@ -105,7 +105,7 @@ impl From<cli_options::SingleSpeed> for SingleSpeed {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SingleColour {
pub colour: Colour,
}
@@ -117,7 +117,7 @@ impl From<cli_options::SingleColour> for SingleColour {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct MultiColour {
pub colour1: Colour,
pub colour2: Colour,
@@ -135,7 +135,7 @@ impl From<cli_options::MultiColour> for MultiColour {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SingleSpeedDirection {
pub direction: Direction,
pub speed: Speed,
@@ -149,7 +149,7 @@ impl From<cli_options::SingleSpeedDirection> for SingleSpeedDirection {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct SingleColourSpeed {
pub colour: Colour,
pub speed: Speed,
@@ -163,7 +163,7 @@ impl From<cli_options::SingleColourSpeed> for SingleColourSpeed {
}
}
#[derive(Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum AuraModes {
Static(SingleColour),
Breathe(TwoColourSpeed),

View File

@@ -5,10 +5,14 @@ use std::str::FromStr;
#[derive(Options)]
pub struct LedBrightness {
level: u8,
level: Option<u8>,
}
impl LedBrightness {
pub fn level(&self) -> u8 {
pub fn new(level: Option<u8>) -> Self {
LedBrightness { level }
}
pub fn level(&self) -> Option<u8> {
self.level
}
}
@@ -18,17 +22,31 @@ impl FromStr for LedBrightness {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"off" => Ok(LedBrightness { level: 0x00 }),
"low" => Ok(LedBrightness { level: 0x01 }),
"med" => Ok(LedBrightness { level: 0x02 }),
"high" => Ok(LedBrightness { level: 0x03 }),
"off" => Ok(LedBrightness { level: Some(0x00) }),
"low" => Ok(LedBrightness { level: Some(0x01) }),
"med" => Ok(LedBrightness { level: Some(0x02) }),
"high" => Ok(LedBrightness { level: Some(0x03) }),
_ => {
println!("Missing required argument, must be one of:\noff,low,med,high\n");
print!(
"{}\n{}\n",
"Invalid argument, must be one of:", "off, low, med, high"
);
Err(AuraError::ParseBrightness)
}
}
}
}
impl ToString for LedBrightness {
fn to_string(&self) -> String {
let s = match self.level {
Some(0x00) => "low",
Some(0x01) => "med",
Some(0x02) => "high",
_ => "unknown",
};
s.to_string()
}
}
#[derive(Deserialize, Serialize)]
pub struct Colour(pub u8, pub u8, pub u8);
@@ -213,3 +231,57 @@ impl Default for SetAuraBuiltin {
})
}
}
#[derive(Copy, Clone, Debug)]
pub enum AniMeStatusValue {
On,
Off,
}
impl FromStr for AniMeStatusValue {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"on" => Ok(AniMeStatusValue::On),
"off" => Ok(AniMeStatusValue::Off),
_ => {
print!("{}\n{}\n", "Invalid argument, must be one of:", "on, off");
Err(AuraError::ParseAnime)
}
}
}
}
impl From<AniMeStatusValue> for bool {
fn from(value: AniMeStatusValue) -> Self {
match value {
AniMeStatusValue::On => true,
AniMeStatusValue::Off => false,
}
}
}
#[derive(Options)]
pub struct AniMeLeds {
#[options(help = "print help message")]
help: bool,
#[options(
no_long,
required,
short = "b",
meta = "BYTE",
help = "set all leds brightness value"
)]
led_brightness: u8,
}
impl AniMeLeds {
pub fn led_brightness(&self) -> u8 {
self.led_brightness
}
}
#[derive(Options)]
pub enum AniMeActions {
#[options(help = "change all leds brightness")]
Leds(AniMeLeds),
}

View File

@@ -1,56 +1,197 @@
use super::*;
use crate::cli_options::LedBrightness;
use crate::fancy::KeyColourArray;
use crate::profile::ProfileEvent;
use dbus::channel::Sender;
use dbus::{blocking::Connection, channel::Token, Message};
use dbus::{blocking::Connection, Message};
use std::error::Error;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
Arc, Mutex,
};
use std::{thread, time::Duration};
use crate::dbus_charge::{
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonCharge, OrgAsuslinuxDaemonNotifyCharge,
};
use crate::dbus_gfx::{
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonGfx, OrgAsuslinuxDaemonNotifyAction,
OrgAsuslinuxDaemonNotifyGfx,
};
use crate::dbus_ledmode::{
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonLed, OrgAsuslinuxDaemonNotifyLed,
};
use crate::dbus_profile::{
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonProfile, OrgAsuslinuxDaemonNotifyProfile,
};
use crate::dbus_rogbios::OrgAsuslinuxDaemon as OrgAsuslinuxDaemonRogBios;
use crate::dbus_supported::OrgAsuslinuxDaemon as OrgAsuslinuxDaemonSupported;
// Signals separated out
pub struct CtrlSignals {
pub gfx_vendor_signal: Arc<Mutex<Option<String>>>,
pub gfx_action_signal: Arc<Mutex<Option<String>>>,
pub profile_signal: Arc<Mutex<Option<String>>>,
pub ledmode_signal: Arc<Mutex<Option<AuraModes>>>,
pub charge_signal: Arc<Mutex<Option<u8>>>,
}
impl CtrlSignals {
#[inline]
pub fn new(connection: &Connection) -> Result<Self, Box<dyn Error>> {
let proxy = connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Gfx",
Duration::from_secs(2),
);
let gfx_vendor_signal = Arc::new(Mutex::new(None));
let gfx_res1 = gfx_vendor_signal.clone();
let _x = proxy.match_signal(
move |sig: OrgAsuslinuxDaemonNotifyGfx, _: &Connection, _: &Message| {
if let Ok(mut lock) = gfx_res1.lock() {
*lock = Some(sig.vendor);
}
true
},
)?;
let gfx_action_signal = Arc::new(Mutex::new(None));
let gfx_res1 = gfx_action_signal.clone();
let _x = proxy.match_signal(
move |sig: OrgAsuslinuxDaemonNotifyAction, _: &Connection, _: &Message| {
if let Ok(mut lock) = gfx_res1.lock() {
*lock = Some(sig.action);
}
true
},
)?;
//
let proxy = connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Profile",
Duration::from_secs(2),
);
let profile_signal = Arc::new(Mutex::new(None));
let prof_res1 = profile_signal.clone();
let _x = proxy.match_signal(
move |sig: OrgAsuslinuxDaemonNotifyProfile, _: &Connection, _: &Message| {
if let Ok(mut lock) = prof_res1.lock() {
*lock = Some(sig.profile);
}
true
},
)?;
//
let proxy = connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
let ledmode_signal = Arc::new(Mutex::new(None));
let led_res1 = ledmode_signal.clone();
let _x = proxy.match_signal(
move |sig: OrgAsuslinuxDaemonNotifyLed, _: &Connection, _: &Message| {
if let Ok(mut lock) = led_res1.lock() {
if let Ok(dat) = serde_json::from_str(&sig.data) {
*lock = Some(dat);
}
}
true
},
)?;
//
let proxy = connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Charge",
Duration::from_secs(2),
);
let charge_signal = Arc::new(Mutex::new(None));
let charge_res1 = charge_signal.clone();
let _x = proxy.match_signal(
move |sig: OrgAsuslinuxDaemonNotifyCharge, _: &Connection, _: &Message| {
if let Ok(mut lock) = charge_res1.lock() {
*lock = Some(sig.limit);
}
true
},
)?;
Ok(CtrlSignals {
gfx_vendor_signal,
gfx_action_signal,
profile_signal,
ledmode_signal,
charge_signal,
})
}
}
/// Simplified way to write a effect block
pub struct AuraDbusClient {
connection: Box<Connection>,
block_time: u64,
stop: Arc<AtomicBool>,
stop_token: Token,
signals: CtrlSignals,
}
impl AuraDbusClient {
#[inline]
pub fn new() -> Result<Self, Box<dyn Error>> {
let connection = Connection::new_system()?;
let stop = Arc::new(AtomicBool::new(false));
let stopper2 = stop.clone();
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "KeyBacklightChanged");
let stop_token = connection.add_match(match_rule, move |_: (), _, msg| {
let stop = Arc::new(AtomicBool::new(false));
let match_rule = dbus::message::MatchRule::new_signal(DBUS_IFACE, "NotifyLed");
let stop1 = stop.clone();
connection.add_match(match_rule, move |_: (), _, msg| {
if msg.read1::<&str>().is_ok() {
stopper2.store(true, Ordering::Relaxed);
stop1.clone().store(true, Ordering::Relaxed);
}
true
})?;
let signals = CtrlSignals::new(&connection)?;
Ok(AuraDbusClient {
connection: Box::new(connection),
block_time: 33333,
stop,
stop_token,
signals,
})
}
pub fn wait_gfx_changed(&self) -> Result<String, Box<dyn Error>> {
loop {
self.connection.process(Duration::from_millis(1))?;
if let Ok(lock) = self.signals.gfx_action_signal.lock() {
if let Some(stuff) = lock.as_ref() {
return Ok(stuff.to_string());
}
}
}
}
/// This method must always be called before the very first write to initialise
/// the keyboard LED EC in the correct mode
#[inline]
pub fn init_effect(&self) -> Result<(), Box<dyn std::error::Error>> {
let mode = AuraModes::PerKey(vec![vec![]]);
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(&mode)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
proxy.set_led_mode(&serde_json::to_string(&mode)?)?;
Ok(())
}
@@ -69,15 +210,13 @@ impl AuraDbusClient {
vecs.push(v.to_vec());
}
let mode = AuraModes::PerKey(vecs);
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(&mode)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
self.write_keyboard_leds(&mode)?;
thread::sleep(Duration::from_micros(self.block_time));
self.connection.process(Duration::from_micros(500))?;
if self.stop.load(Ordering::Relaxed) {
self.connection.remove_match(self.stop_token)?;
println!("Keyboard backlight was changed, exiting");
std::process::exit(1)
}
@@ -86,20 +225,89 @@ impl AuraDbusClient {
#[inline]
pub fn write_keyboard_leds(&self, mode: &AuraModes) -> Result<(), Box<dyn std::error::Error>> {
let mut msg =
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
.append1(serde_json::to_string(mode)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
proxy.set_led_mode(&serde_json::to_string(mode)?)?;
Ok(())
}
#[inline]
pub fn next_keyboard_led_mode(&self) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
proxy.next_led_mode()?;
Ok(())
}
#[inline]
pub fn prev_keyboard_led_mode(&self) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
proxy.prev_led_mode()?;
Ok(())
}
#[inline]
pub fn get_gfx_pwr(&self) -> Result<String, Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Gfx",
Duration::from_secs(2),
);
let x = proxy.power()?;
Ok(x)
}
#[inline]
pub fn get_gfx_mode(&self) -> Result<String, Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Gfx",
Duration::from_secs(2),
);
let x = proxy.vendor()?;
Ok(x)
}
#[inline]
pub fn write_gfx_mode(&self, vendor: String) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Gfx",
Duration::from_secs(30),
);
proxy.set_vendor(&vendor)?;
Ok(())
}
#[inline]
pub fn next_fan_profile(&self) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Profile",
Duration::from_secs(2),
);
proxy.next_profile()?;
Ok(())
}
#[inline]
pub fn write_fan_mode(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ProfileCommand")?
.append1(serde_json::to_string(&ProfileEvent::ChangeMode(level))?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Profile",
Duration::from_secs(2),
);
proxy.set_profile(&serde_json::to_string(&ProfileEvent::ChangeMode(level))?)?;
Ok(())
}
@@ -108,19 +316,23 @@ impl AuraDbusClient {
&self,
cmd: &ProfileEvent,
) -> Result<(), Box<dyn std::error::Error>> {
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "ProfileCommand")?
.append1(serde_json::to_string(cmd)?);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Profile",
Duration::from_secs(2),
);
proxy.set_profile(&serde_json::to_string(cmd)?)?;
Ok(())
}
#[inline]
pub fn write_charge_limit(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetChargeLimit")?
.append1(level);
msg.set_no_reply(true);
self.connection.send(msg).unwrap();
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Charge",
Duration::from_secs(2),
);
proxy.set_limit(level)?;
Ok(())
}
@@ -130,8 +342,77 @@ impl AuraDbusClient {
}
#[inline]
pub fn write_brightness(&self, level: u8) -> Result<String, Box<dyn std::error::Error>> {
pub fn get_led_brightness(&self) -> Result<LedBrightness, Box<dyn Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Led",
Duration::from_secs(2),
);
match proxy.led_brightness()? {
-1 => Ok(LedBrightness::new(None)),
level => Ok(LedBrightness::new(Some(level as u8))),
}
}
#[inline]
pub fn write_brightness(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
self.write_keyboard_leds(&AuraModes::LedBrightness(level))?;
Ok(String::new())
Ok(())
}
//
#[inline]
pub fn get_bios_dedicated_gfx(&self) -> Result<i16, Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/RogBios",
Duration::from_secs(2),
);
let x = proxy.dedicated_graphic_mode()?;
Ok(x)
}
#[inline]
pub fn set_bios_dedicated_gfx(&self, on: bool) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/RogBios",
Duration::from_secs(2),
);
proxy.set_dedicated_graphic_mode(<bool>::from(on))?;
Ok(())
}
#[inline]
pub fn get_bios_post_sound(&self) -> Result<i16, Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/RogBios",
Duration::from_secs(2),
);
let x = proxy.post_boot_sound()?;
Ok(x)
}
#[inline]
pub fn set_bios_post_sound(&self, on: bool) -> Result<(), Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/RogBios",
Duration::from_secs(2),
);
proxy.set_post_boot_sound(<bool>::from(on))?;
Ok(())
}
#[inline]
pub fn get_supported_functions(&self) -> Result<String, Box<dyn std::error::Error>> {
let proxy = self.connection.with_proxy(
"org.asuslinux.Daemon",
"/org/asuslinux/Supported",
Duration::from_secs(2),
);
let x = proxy.supported_functions()?;
Ok(x)
}
}

27
asus-nb/src/dbus_anime.rs Normal file
View File

@@ -0,0 +1,27 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Anime -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn set_anime(&self, input: Vec<Vec<u8>>) -> Result<(), dbus::Error>;
fn set_on_off(&self, status: bool) -> Result<(), dbus::Error>;
fn set_boot_on_off(&self, status: bool) -> Result<(), dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn set_anime(&self, input: Vec<Vec<u8>>) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetAnime", (input,))
}
fn set_on_off(&self, status: bool) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetOnOff", (status,))
}
fn set_boot_on_off(&self, status: bool) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetBootOnOff", (status,))
}
}

View File

@@ -0,0 +1,45 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Charge -m None`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn set_limit(&self, limit: u8) -> Result<(), dbus::Error>;
fn limit(&self) -> Result<i16, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn set_limit(&self, limit: u8) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetLimit", (limit,))
}
fn limit(&self) -> Result<i16, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "Limit", ())
.and_then(|r: (i16,)| Ok(r.0))
}
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyCharge {
pub limit: u8,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyCharge {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.limit, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyCharge {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyCharge { limit: i.read()? })
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyCharge {
const NAME: &'static str = "NotifyCharge";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}

73
asus-nb/src/dbus_gfx.rs Normal file
View File

@@ -0,0 +1,73 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Gfx -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn vendor(&self) -> Result<String, dbus::Error>;
fn power(&self) -> Result<String, dbus::Error>;
fn set_vendor(&self, vendor: &str) -> Result<(), dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn vendor(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "Vendor", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn power(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "Power", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn set_vendor(&self, vendor: &str) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetVendor", (vendor,))
}
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyGfx {
pub vendor: String,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyGfx {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.vendor, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyGfx {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyGfx { vendor: i.read()? })
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyGfx {
const NAME: &'static str = "NotifyGfx";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyAction {
pub action: String,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyAction {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.action, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyAction {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyAction { action: i.read()? })
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyAction {
const NAME: &'static str = "NotifyAction";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}

View File

@@ -0,0 +1,67 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Led -m None`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn set_led_mode(&self, data: &str) -> Result<(), dbus::Error>;
fn next_led_mode(&self) -> Result<(), dbus::Error>;
fn prev_led_mode(&self) -> Result<(), dbus::Error>;
fn led_mode(&self) -> Result<String, dbus::Error>;
fn led_modes(&self) -> Result<String, dbus::Error>;
fn led_brightness(&self) -> Result<i16, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn set_led_mode(&self, data: &str) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetLedMode", (data,))
}
fn next_led_mode(&self) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "NextLedMode", ())
}
fn prev_led_mode(&self) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "PrevLedMode", ())
}
fn led_mode(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "LedMode", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn led_modes(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "LedModes", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn led_brightness(&self) -> Result<i16, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "LedBrightness", ())
.and_then(|r: (i16,)| Ok(r.0))
}
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyLed {
pub data: String,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyLed {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.data, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyLed {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyLed { data: i.read()? })
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyLed {
const NAME: &'static str = "NotifyLed";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}

View File

@@ -0,0 +1,62 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -p /org/asuslinux/Profile -m None -f org.asuslinux.Daemon -c blocking`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn set_profile(&self, profile: &str) -> Result<(), dbus::Error>;
fn next_profile(&self) -> Result<(), dbus::Error>;
fn active_profile_name(&self) -> Result<String, dbus::Error>;
fn profile(&self) -> Result<String, dbus::Error>;
fn profiles(&self) -> Result<String, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn set_profile(&self, profile: &str) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetProfile", (profile,))
}
fn next_profile(&self) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "NextProfile", ())
}
fn active_profile_name(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "ActiveProfileName", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn profile(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "Profile", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn profiles(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "Profiles", ())
.and_then(|r: (String,)| Ok(r.0))
}
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyProfile {
pub profile: String,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyProfile {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.profile, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyProfile {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyProfile { profile: i.read()? })
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyProfile {
const NAME: &'static str = "NotifyProfile";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}

View File

@@ -0,0 +1,86 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/RogBios -m None -o asus-nb/src/dbus_rogbios.rs`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn set_dedicated_graphic_mode(&self, dedicated: bool) -> Result<(), dbus::Error>;
fn dedicated_graphic_mode(&self) -> Result<i16, dbus::Error>;
fn set_post_boot_sound(&self, on: bool) -> Result<(), dbus::Error>;
fn post_boot_sound(&self) -> Result<i16, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn set_dedicated_graphic_mode(&self, dedicated: bool) -> Result<(), dbus::Error> {
self.method_call(
"org.asuslinux.Daemon",
"SetDedicatedGraphicMode",
(dedicated,),
)
}
fn dedicated_graphic_mode(&self) -> Result<i16, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "DedicatedGraphicMode", ())
.and_then(|r: (i16,)| Ok(r.0))
}
fn set_post_boot_sound(&self, on: bool) -> Result<(), dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SetPostBootSound", (on,))
}
fn post_boot_sound(&self) -> Result<i16, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "PostBootSound", ())
.and_then(|r: (i16,)| Ok(r.0))
}
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyDedicatedGraphicMode {
pub dedicated: bool,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyDedicatedGraphicMode {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.dedicated, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyDedicatedGraphicMode {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyDedicatedGraphicMode {
dedicated: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyDedicatedGraphicMode {
const NAME: &'static str = "NotifyDedicatedGraphicMode";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}
#[derive(Debug)]
pub struct OrgAsuslinuxDaemonNotifyPostBootSound {
pub dedicated: bool,
}
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyPostBootSound {
fn append(&self, i: &mut arg::IterAppend) {
arg::RefArg::append(&self.dedicated, i);
}
}
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyPostBootSound {
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgAsuslinuxDaemonNotifyPostBootSound {
dedicated: i.read()?,
})
}
}
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyPostBootSound {
const NAME: &'static str = "NotifyPostBootSound";
const INTERFACE: &'static str = "org.asuslinux.Daemon";
}

View File

@@ -0,0 +1,18 @@
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Supported -m None -o asus-nb/src/dbus_supported.rs`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgAsuslinuxDaemon {
fn supported_functions(&self) -> Result<String, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgAsuslinuxDaemon
for blocking::Proxy<'a, C>
{
fn supported_functions(&self) -> Result<String, dbus::Error> {
self.method_call("org.asuslinux.Daemon", "SupportedFunctions", ())
.and_then(|r: (String,)| Ok(r.0))
}
}

View File

@@ -1,10 +1,13 @@
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum AuraError {
ParseColour,
ParseSpeed,
ParseDirection,
ParseBrightness,
ParseAnime,
}
impl fmt::Display for AuraError {
@@ -15,6 +18,25 @@ impl fmt::Display for AuraError {
AuraError::ParseSpeed => write!(f, "Could not parse speed"),
AuraError::ParseDirection => write!(f, "Could not parse direction"),
AuraError::ParseBrightness => write!(f, "Could not parse brightness"),
AuraError::ParseAnime => write!(f, "Could not parse anime"),
}
}
}
impl Error for AuraError {}
#[derive(Debug)]
pub enum GraphicsError {
ParseVendor,
}
impl fmt::Display for GraphicsError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GraphicsError::ParseVendor => write!(f, "Could not parse vendor name"),
}
}
}
impl Error for GraphicsError {}

View File

@@ -25,6 +25,14 @@ pub mod anime_matrix;
pub mod error;
pub mod dbus_anime;
pub mod dbus_charge;
pub mod dbus_gfx;
pub mod dbus_ledmode;
pub mod dbus_profile;
pub mod dbus_rogbios;
pub mod dbus_supported;
// static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
// static LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
// static LED_INIT3: [u8; 6] = [0x5d, 0x05, 0x20, 0x31, 0, 0x08];

View File

@@ -7,6 +7,7 @@ use std::str::FromStr;
pub enum ProfileEvent {
Cli(ProfileCommand),
ChangeMode(u8),
Toggle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -74,7 +75,9 @@ fn parse_fan_curve(data: &str) -> Result<Curve, String> {
#[derive(Debug, Clone, Options, Serialize, Deserialize)]
pub struct ProfileCommand {
#[options(help = "print help message")]
help: bool,
pub help: bool,
#[options(help = "toggle to next profile in list")]
pub next: bool,
#[options(help = "create the profile if it doesn't exist")]
pub create: bool,

21
asus-notify/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "asus-notify"
version = "2.0.4"
authors = ["Luke D Jones <luke@ljones.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# serialisation
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
dbus = { version = "^0.8" }
asus-nb = { path = "../asus-nb" }
asus-nb-ctrl = { path = "../asus-nb-ctrl" }
[dependencies.notify-rust]
version = "^4.0.0"
default-features = false
features = ["z"]

107
asus-notify/src/main.rs Normal file
View File

@@ -0,0 +1,107 @@
use asus_nb::core_dbus::CtrlSignals;
use daemon::config::{Config, Profile};
use dbus::blocking::Connection;
use notify_rust::{Hint, Notification, NotificationHandle};
use std::error::Error;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Version {}", env!("CARGO_PKG_VERSION"));
let mut cfg = Config::read_new()?;
let mut last_profile = String::new();
let connection = Connection::new_system()?;
let signals = CtrlSignals::new(&connection)?;
let mut last_profile_notif: Option<NotificationHandle> = None;
let mut last_led_notif: Option<NotificationHandle> = None;
let mut last_gfx_notif: Option<NotificationHandle> = None;
let mut last_chrg_notif: Option<NotificationHandle> = None;
loop {
std::thread::sleep(Duration::from_millis(100));
connection.process(std::time::Duration::from_millis(200))?;
if let Ok(mut lock) = signals.gfx_vendor_signal.lock() {
if let Some(vendor) = lock.take() {
if let Some(notif) = last_gfx_notif.take() {
notif.close();
}
let x = do_notif(&format!("Graphics mode changed to {}", vendor))?;
last_gfx_notif = Some(x);
}
}
if let Ok(mut lock) = signals.charge_signal.lock() {
if let Some(limit) = lock.take() {
if let Some(notif) = last_chrg_notif.take() {
notif.close();
}
let x = do_notif(&format!("Battery charge limit changed to {}", limit))?;
last_led_notif = Some(x);
}
}
if let Ok(mut lock) = signals.ledmode_signal.lock() {
if let Some(ledmode) = lock.take() {
if let Some(notif) = last_led_notif.take() {
notif.close();
}
let x = do_notif(&format!(
"Keyboard LED mode changed to {}",
<&str>::from(&ledmode)
))?;
last_led_notif = Some(x);
}
}
// We need to do the config read because of a limitation preventing
// easy dbus notification from the profile controller
cfg.read();
if last_profile != cfg.active_profile {
if let Some(notif) = last_profile_notif.take() {
notif.close();
}
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
let x = do_thermal_notif(&profile, &cfg.active_profile)?;
last_profile_notif = Some(x);
last_profile = cfg.active_profile.clone();
}
}
}
}
fn do_thermal_notif(profile: &Profile, label: &str) -> Result<NotificationHandle, Box<dyn Error>> {
let fan = profile.fan_preset;
let turbo = if profile.turbo { "enabled" } else { "disabled" };
let icon = match fan {
0 => "asus_notif_yellow",
1 => "asus_notif_red",
2 => "asus_notif_green",
_ => "asus_notif_red",
};
let x = Notification::new()
.summary("ASUS ROG")
.body(&format!(
"Thermal profile changed to {}, turbo {}",
label.to_uppercase(),
turbo
))
.hint(Hint::Resident(true))
.timeout(2000)
.hint(Hint::Category("device".into()))
//.hint(Hint::Transient(true))
.icon(icon)
.show()?;
Ok(x)
}
fn do_notif(body: &str) -> Result<NotificationHandle, Box<dyn Error>> {
let x = Notification::new()
.summary("ASUS ROG")
.body(body)
.timeout(2000)
.show()?;
Ok(x)
}

View 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"

View File

@@ -0,0 +1,4 @@
Section "ServerLayout"
Identifier "layout"
Option "AllowNVIDIAGPUScreens"
EndSection

50
data/_asusctl Normal file
View 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
View 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
View 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

View File

@@ -3,6 +3,16 @@ 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 = "Zephyrus"
board_names = ["GM501GM", "GX531"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,37 +0,0 @@
---
drivers/platform/x86/asus-wmi.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f10ec9d745e5..469f1a852719 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1780,6 +1780,15 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
return 0;
}
+static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
+{
+ if (!asus->throttle_thermal_policy_available)
+ return 0;
+
+ asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
+ return throttle_thermal_policy_write(asus);
+}
+
static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
{
u8 new_mode = asus->throttle_thermal_policy_mode + 1;
@@ -2548,6 +2557,8 @@ static int asus_wmi_add(struct platform_device *pdev)
err = throttle_thermal_policy_check_present(asus);
if (err)
goto fail_throttle_thermal_policy;
+ else
+ throttle_thermal_policy_set_default(asus);
err = asus_wmi_sysfs_init(asus->platform_device);
if (err)
--
2.24.0

File diff suppressed because it is too large Load Diff

View File

@@ -1,63 +0,0 @@
[root@rog tmp]# cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
[root@rog tmp]# iasl -d dsdt.dat
Intel ACPI Component Architecture
ASL+ Optimizing Compiler/Disassembler version 20200717
Copyright (c) 2000 - 2020 Intel Corporation
File appears to be binary: found 97272 non-ASCII characters, disassembling
Binary file appears to be a valid ACPI table, disassembling
Input file dsdt.dat, Length 0x45955 (285013) bytes
ACPI: DSDT 0x0000000000000000 045955 (v02 _ASUS_ Notebook 01072009 INTL 20160527)
Pass 1 parse of [DSDT]
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload-495)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
Pass 2 parse of [DSDT]
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload2-479)
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions)
Parsing completed
Warning - Emitting ASL code "External (ESPC)"
This is a conflicting declaration with some other declaration within the ASL code.
This external declaration may need to be deleted in order to recompile the dsl file.
Warning - Emitting ASL code "External (PSON)"
This is a conflicting declaration with some other declaration within the ASL code.
This external declaration may need to be deleted in order to recompile the dsl file.
Disassembly completed
ASL Output: dsdt.dsl - 2006627 bytes
[root@rog tmp]#

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +0,0 @@
Product name: ROG Strix G531GT_G531GT
Product family: ROG Strix
Board name: G531GT
lsusb | grep 0b05: Bus 001 Device 004: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
1b00a00964188090ffff00000000090000020002000401400000005dbc00010101001000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000 ROGKEY
1b006079b90e8090ffff00000000090000020002000401400000005dbc00010101601000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ENTER
1b00e014ee088090ffff00000000090000020002000401400000005dbc00010101701000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000 RSHIFT

Binary file not shown.

Binary file not shown.

View File

@@ -1,130 +0,0 @@
Bus 001 Device 004: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0b05 ASUSTek Computer, Inc.
idProduct 0x1866
bcdDevice 0.02
iManufacturer 1 ASUSTeK Computer Inc.
iProduct 2 N-KEY Device
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x005b
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xe0
Self Powered
Remote Wakeup
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 3 (error)
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 67
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 1 ASUSTeK Computer Inc.
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 65
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 1 ASUSTeK Computer Inc.
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 167
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Device Status: 0x0001
Self Powered

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

View File

@@ -1,6 +0,0 @@
1b00a0e94d068090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000 LED6
1b0020d77e048090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000 LED5
1b008089e1048090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000 LED4
1b0010103d088090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000 LED3
1b007079ce1f8090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 LED2
1b00e0e4fe078090ffff00000000090000020002000401400000005dbc00010400000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 LED1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

View File

@@ -1,130 +0,0 @@
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0b05 ASUSTek Computer, Inc.
idProduct 0x1866
bcdDevice 0.02
iManufacturer 1 ASUSTeK Computer Inc.
iProduct 2 N-KEY Device
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x005b
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xe0
Self Powered
Remote Wakeup
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 3 (error)
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 83
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 1 ASUSTeK Computer Inc.
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 65
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 1 Keyboard
iInterface 1 ASUSTeK Computer Inc.
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.10
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 167
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Device Status: 0x0001
Self Powered

View File

@@ -1,50 +0,0 @@
artem@art-kub:~$ cat /etc/*-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04 LTS"
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
artem@art-kub:~$ hostnamectl
Static hostname: art-kub
Icon name: computer-laptop
Chassis: laptop
Machine ID: f3792a953d24486bb8881f4ab1b93e44
Boot ID: 5f064ae70f274461909bea1526a5b4e3
Operating System: Ubuntu 20.04 LTS
Kernel: Linux 5.4.0-39-generic
Architecture: x86-64
artem@art-kub:~$ cat /proc/version
Linux version 5.4.0-39-generic (buildd@lcy01-amd64-016) (gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2)) #43-Ubuntu SMP Fri Jun 19 10:28:31 UTC 2020
artem@art-kub:~$ uname -a
Linux art-kub 5.4.0-39-generic #43-Ubuntu SMP Fri Jun 19 10:28:31 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
artem@art-kub:~$ cat /sys/class/dmi/id/product_name
ROG Strix G712LV_G712LV
artem@art-kub:~$ cat /sys/class/dmi/id/product_family
ROG Strix
artem@art-kub:~$ cat /sys/class/dmi/id/board_name
G712LV
artem@art-kub:~$ lsusb |grep 0b05
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device

View File

@@ -1,28 +0,0 @@
001:002:002:DESCRIPTOR 1593321424.949103
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 1593321424.951280
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 1593321424.952588
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

View File

@@ -1,6 +0,0 @@
$ cat /sys/class/dmi/id/product_name
ROG Zephyrus G14 GA401IV_GA401IV
$ cat /sys/class/dmi/id/product_family
ROG Zephyrus G14
$ cat /sys/class/dmi/id/board_name
GA401IV

Some files were not shown because too many files have changed in this diff Show More