Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7419cbc4c | ||
|
|
67ad38a7e6 | ||
|
|
e572ae2c62 | ||
|
|
24962eedc1 | ||
|
|
368d279ca5 | ||
|
|
9e4cc329ed | ||
|
|
d4702a166a | ||
|
|
925c709097 | ||
|
|
411788f72c | ||
|
|
4e4ea0035e | ||
|
|
0063f3f0fc | ||
|
|
fe6231ad4e | ||
|
|
4cdb06959b | ||
|
|
781ad30eb5 | ||
|
|
84d056f2ed | ||
|
|
3a2f2c99f4 | ||
|
|
cddff32757 | ||
|
|
1b427c6c07 | ||
|
|
6ba645f727 | ||
|
|
772538bc8a |
@@ -18,7 +18,7 @@ build:
|
||||
- make && make vendor
|
||||
artifacts:
|
||||
paths:
|
||||
- vendor-$(grep -P 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2).tar.xz
|
||||
- vendor_asus-nb-ctrl_*.tar.xz
|
||||
- cargo-config
|
||||
|
||||
variables:
|
||||
|
||||
41
CHANGELOG.md
@@ -6,6 +6,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
# [2.0.0] - 2020-09-21
|
||||
### Changed
|
||||
- graphics options via CLI are now a command block:
|
||||
+ `asusctl graphics`
|
||||
+ -m Mode <nvidia, hybrid, compute, integrated>
|
||||
+ -g Get current mode
|
||||
+ -f Force reboot or restart display manager without confirmation
|
||||
|
||||
# [2.0.0] - 2020-09-21
|
||||
### Changed
|
||||
- Code refactor to spawn less tasks. Main loop will run only as fast as
|
||||
it receives events
|
||||
- No-longer using tokio or async, reducing resource use
|
||||
### Added
|
||||
- A basic user daemon has been added for user notifications over dbus (XDG spec)
|
||||
- Added a user systemd service for notifications (asus-notify)
|
||||
- Graphics mode handling <iGPU only, dGPU only, or hybrid>, see asusctl --help
|
||||
### BREAKING CHANGES
|
||||
- asusd.conf has changed slightly and will overwrite old configs
|
||||
- All DBUS methods/signals/paths etc, are all updated and changed
|
||||
|
||||
# [1.1.2] - 2020-09-10
|
||||
### Changed
|
||||
- Bump rog-fan-curve to new versiont o support GA401IV
|
||||
|
||||
# [1.1.1] - 2020-09-10
|
||||
### Changed
|
||||
- Correction to AMD turbo setting
|
||||
|
||||
# [1.1.0] - 2020-09-10
|
||||
### Changed
|
||||
- Uses string instead of debug print for some errors
|
||||
- Add interface num arg for LED controller (should help support
|
||||
older laptops better)
|
||||
- Some slightly better error messages
|
||||
- Fix an idiotic mistake in `for i in 0..2.. if i > 0` -_-
|
||||
- Remove "unsupported" warning on laptop ctrl
|
||||
- Silence warning about AniMe not existing
|
||||
- Adjust the turbo-toggle CLI arg
|
||||
- Version bump for new release with fancurves
|
||||
|
||||
## [1.0.2] - 2020-08-13
|
||||
### Changed
|
||||
- Bugfixes to led brightness watcher
|
||||
|
||||
850
Cargo.lock
generated
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["asus-nb-ctrl", "asus-nb"]
|
||||
members = ["asus-notify", "asus-nb-ctrl", "asus-nb", "ctrl-gfx"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -13,4 +13,4 @@ opt-level = 1
|
||||
|
||||
[profile.bench]
|
||||
debug = false
|
||||
opt-level = 3
|
||||
opt-level = 3
|
||||
|
||||
25
Makefile
@@ -13,8 +13,11 @@ SRC = Cargo.toml Cargo.lock Makefile $(shell find -type f -wholename '**/src/*.r
|
||||
|
||||
BIN_C=asusctl
|
||||
BIN_D=asusd
|
||||
LEDCONFIG=asusd-ledmodes.toml
|
||||
VERSION:=$(shell grep -P 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2)
|
||||
BIN_N=asus-notify
|
||||
LEDCFG=asusd-ledmodes.toml
|
||||
X11CFG=90-nvidia-screen-G05.conf
|
||||
PMRULES=90-asusd-nvidia-pm.rules
|
||||
VERSION:=$(shell grep -Pm1 'version = "(\d.\d.\d)"' asus-nb-ctrl/Cargo.toml | cut -d'"' -f2)
|
||||
|
||||
DEBUG ?= 0
|
||||
ifeq ($(DEBUG),0)
|
||||
@@ -38,17 +41,29 @@ distclean:
|
||||
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 0755 "target/release/$(BIN_N)" "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||
install -D -m 0644 "data/$(PMRULES)" "$(DESTDIR)/lib/udev/rules.d/$(PMRULES)"
|
||||
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/$(LEDCFG)" "$(DESTDIR)$(sysconfdir)/asusd/$(LEDCFG)"
|
||||
install -D -m 0644 "data/$(BIN_D).conf" "$(DESTDIR)$(sysconfdir)/dbus-1/system.d/$(BIN_D).conf"
|
||||
install -D -m 0644 "data/$(X11CFG)" "$(DESTDIR)$(sysconfdir)/X11/xorg.conf.d/$(X11CFG)"
|
||||
install -D -m 0644 "data/$(BIN_D).service" "$(DESTDIR)/lib/systemd/system/$(BIN_D).service"
|
||||
install -D -m 0644 "data/$(BIN_N).service" "$(DESTDIR)/lib/systemd/user/$(BIN_N).service"
|
||||
install -D -m 0644 "data/icons/asus_notif_yellow.png" "$(DESTDIR)/usr/share/icons/hicolor/512x512/apps/asus_notif_yellow.png"
|
||||
install -D -m 0644 "data/icons/asus_notif_green.png" "$(DESTDIR)/usr/share/icons/hicolor/512x512/apps/asus_notif_green.png"
|
||||
install -D -m 0644 "data/icons/asus_notif_red.png" "$(DESTDIR)/usr/share/icons/hicolor/512x512/apps/asus_notif_red.png"
|
||||
|
||||
uninstall:
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_N)"
|
||||
rm -f "$(DESTDIR)/lib/udev/rules.d/$(PMRULES)"
|
||||
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)$(sysconfdir)/X11/xorg.conf.d/$(X11CFG)"
|
||||
rm -f "$(DESTDIR)/lib/systemd/system/$(BIN_D).service"
|
||||
rm -r "$(DESTDIR)/lib/systemd/user/$(BIN_N).service"
|
||||
rm -r "$(DESTDIR)/usr/share/icons/hicolor/512x512/apps/asus_notif_*"
|
||||
|
||||
update:
|
||||
cargo update
|
||||
@@ -59,12 +74,12 @@ vendor:
|
||||
echo 'directory = "vendor"' >> .cargo/config
|
||||
mv .cargo/config ./cargo-config
|
||||
rm -rf .cargo
|
||||
tar pcfJ vendor-$(VERSION).tar.xz vendor
|
||||
tar pcfJ vendor_asus-nb-ctrl_$(VERSION).tar.xz vendor
|
||||
rm -rf vendor
|
||||
|
||||
target/release/$(BIN_D): $(SRC)
|
||||
ifeq ($(VENDORED),1)
|
||||
@echo "version = $(VERSION)"
|
||||
tar pxf vendor-$(VERSION).tar.xz
|
||||
tar pxf vendor_asus-nb-ctrl_$(VERSION).tar.xz
|
||||
endif
|
||||
cargo build $(ARGS)
|
||||
|
||||
249
README.md
@@ -1,10 +1,13 @@
|
||||
# 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 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 +15,89 @@ 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)
|
||||
|
||||
## SUPPORTED LAPTOPS
|
||||
|
||||
If your laptop is not in the following lists, it may still work with fan-mode switching and charge limit control.
|
||||
Most ASUS gaming laptops that have a USB keyboard. If `lsusb` shows something similar
|
||||
to this:
|
||||
|
||||
**Please help test or provide info for:**
|
||||
```
|
||||
Bus 001 Device 002: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
```
|
||||
|
||||
- GL703(0x1869)
|
||||
- GL553/GL753 (device = 0x1854) (attempted support from researching 2nd-hand info, multizone may work)
|
||||
then it may work without tweaks. Technically all other functions except the LED
|
||||
and AniMe parts should work regardless of your latop make. Eventually this project
|
||||
will probably suffer another rename once it becomes generic enough to do so.
|
||||
|
||||
**Laptop support is modified on a per-case basis** as the EC for the keyboard varies
|
||||
a little between models, e.g, some RGB modes are missing, or it's a single colour.
|
||||
As far as I can see, the EC does not give us a way to find what modes are supported.
|
||||
## Implemented
|
||||
|
||||
### ANIME AND OTHER FUNCTIONS
|
||||
- [X] System daemon
|
||||
- [X] User notifications daemon
|
||||
- [X] Setting/modifying built-in LED modes
|
||||
- [X] Per-key LED setting
|
||||
- [X] Fancy LED modes (See examples)
|
||||
- [X] Saving settings for reload
|
||||
- [X] Logging - required for journalctl
|
||||
- [X] AniMatrix display on G14 models that include it
|
||||
- [X] Set battery charge limit (with kernel supporting this)
|
||||
- [X] Fancy fan control on G14 + G15 thanks to @Yarn1
|
||||
- [X] Graphics mode switching between iGPU, dGPU, and On-Demand
|
||||
|
||||
**AniMe device check is performed on start, if your device has one it will be detected.**
|
||||
# FUNCTIONS
|
||||
|
||||
**NOTE:** If charge limit or fan modes are not working, then you may require a kernel newer than 5.6.10.
|
||||
## Graphics switching
|
||||
|
||||
- [X] AniMe Matrix display
|
||||
- [X] Power profile switching on fan-mode (FN+F5)
|
||||
- [X] Intel
|
||||
- [X] Turbo enale/disable
|
||||
- [X] Min frequency percentage
|
||||
- [X] Max frequency percentage
|
||||
- [X] AMD
|
||||
- [X] Turbo enale/disable
|
||||
- [X] Battery charge limit
|
||||
A new feature has been added to enable switching graphics modes. This can be disabled
|
||||
in the config with `"manage_gfx": false,`. Please be aware it is a work in progress.
|
||||
|
||||
**NOTE:** GA14/GA401 and GA15/GA502/GU502, You will need kernel [patches](https://lab.retarded.farm/zappel/asus-rog-zephyrus-g14/-/tree/master/kernel_patches).
|
||||
The CLI option for this does not require root until it asks for it, and provides
|
||||
instructions.
|
||||
|
||||
### KEYBOARD BACKLIGHT MODES
|
||||
This switcher conflicts with other gpu switchers like optimus-manager, suse-prime
|
||||
or ubuntu-prime, system76-power, and bbswitch. If you have issues with `asusd`
|
||||
always defaulting to `integrated` mode on boot then you will need to check for
|
||||
stray configs blocking nvidia modules from loading in:
|
||||
- `/etc/modprobe.d/`
|
||||
- `/usr/lib/modprope.d/`
|
||||
|
||||
### Power management udev rule
|
||||
|
||||
If you have installed the Nvidia driver manually you will require the
|
||||
`data/90-asusd-nvidia-pm.rules` udev rule to be installed in `/etc/udev/rules.d/`.
|
||||
|
||||
The above seems to also apply to Arch in general as it leaves a lot of things up
|
||||
to the user.
|
||||
|
||||
### fedora and openSUSE
|
||||
|
||||
You *may* need a file `/etc/dracut.conf.d/90-nvidia-dracut-G05.conf` installed
|
||||
to stop dracut including the nvidia modules in the ramdisk.
|
||||
|
||||
```
|
||||
# filename /etc/dracut.conf.d/90-nvidia-dracut-G05.conf
|
||||
# Omit the nvidia driver from the ramdisk, to avoid needing to regenerate
|
||||
# the ramdisk on updates, and to ensure the power-management udev rules run
|
||||
# on module load
|
||||
omit_drivers+=" nvidia nvidia-drm nvidia-modeset nvidia-uvm "
|
||||
```
|
||||
|
||||
and run `dracut -f` after creating it.
|
||||
|
||||
## KEYBOARD BACKLIGHT MODES
|
||||
|
||||
Models GA401, GA502, GU502 support LED brightness change only (no RGB).
|
||||
|
||||
| MODEL | STATIC | BREATHING | STROBE | RAINBOW | STAR | RAIN | HIGHLIGHT | LASER | RIPPLE | PULSE | COMET | FLASH | ZONES | PER-KEY RGB |
|
||||
|:------:|:------:|:---------:|:------:|:-------:|:----:|:----:|:---------:|:-----:|:------:|:-----:|:-----:|:-----:|:-----:|:-----------:|
|
||||
| G512LI | X | X | X | X | | | | | | | | | | |
|
||||
| G712LI | X | X | X | X | | | | | | | | | | |
|
||||
| GM501 | X | X | X | X | | | | | | | | | X | |
|
||||
| GX531 | X | X | X | X | | | | | | | | | X | |
|
||||
| G512 | X | X | X | X | | | | | | | | | X | |
|
||||
| G712 | X | X | X | X | | | | | | | | | X | |
|
||||
| GX502 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
| GX701 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
| G531 | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
||||
| G731 | X | X | X | X | X | X | X | X | X | X | X | X | X | X |
|
||||
| G532 | X | X | X | X | X | X | X | X | X | X | X | X | | X |
|
||||
|
||||
It is highly likely this doesn't cover all models.
|
||||
|
||||
For editing the `/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as follows:
|
||||
If you model isn't getting the correct led modes, you can edit the file
|
||||
`/etc/asusd/asusd-ledmodes.toml`, the LED Mode numbers are as follows:
|
||||
|
||||
```
|
||||
0 STATIC
|
||||
@@ -97,18 +116,11 @@ 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)
|
||||
# BUILDING
|
||||
|
||||
## Requirements for compiling
|
||||
Requirements are:
|
||||
|
||||
- `rustc` + `cargo` + `make`
|
||||
- `libusb-1.0-0-dev`
|
||||
@@ -136,32 +148,36 @@ If you are upgrading from a previous installed version, you will need to restart
|
||||
$ systemctl daemon-reload && systemctl restart asusd
|
||||
```
|
||||
|
||||
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power`
|
||||
gnome-shell extension, or at least limiting use of the power-management parts as `asusd` lets you set the same things
|
||||
(one or the other will overwrite pstates). I will create a shell extension at some point similar to system76, but using
|
||||
the asusd parts. It is safe to leave `system76-power.service` enabled and use for switching between graphics modes.
|
||||
You may also need to activate the service for debian install. If running Pop!_OS, I suggest disabling `system76-power` gnome-shell extension and systemd service.
|
||||
|
||||
If you would like to run this daemon on another non-ASUS laptop you can. You'll
|
||||
have all features available except the LED and AniMe control (further controllers
|
||||
can be added on request). You will need to install the alternative service from
|
||||
`data/asusd-alt.service`.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd.conf`.
|
||||
Run `sudo make uninstall` in the source repo, and remove `/etc/asusd/`.
|
||||
|
||||
## Updating
|
||||
|
||||
Occasionally you need to remove `/etc/asusd.conf` and restart the daemon to create a new one. You *can* back up the old
|
||||
one and copy settings back over (then restart daemon again).
|
||||
If there has been a config file format change your config will be overwritten. This will
|
||||
become less of an issue once the feature set is nailed down. Work is happening to enable
|
||||
parsing of older configs and transferring settings to new.
|
||||
|
||||
# Usage
|
||||
# USAGE
|
||||
|
||||
**NOTE! Fan mode toggling requires a newer kernel**. I'm unsure when the patches required for it got merged - I've
|
||||
tested with the 5.6.6 kernel and above only. To see if the fan-mode changed cat either:
|
||||
**NOTE! Fan mode toggling requires a newer kernel**. I'm unsure when the patches
|
||||
required for it got merged - I've tested with the 5.6.6 kernel and above only.
|
||||
To see if the fan-mode changed cat either:
|
||||
|
||||
- `cat /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy` or
|
||||
- `cat /sys/devices/platform/asus-nb-wmi/fan_boost_mode`
|
||||
|
||||
The numbers are 0 = Normal/Balanced, 1 = Boost, 2 = Silent.
|
||||
|
||||
Running the program as a daemon manually will require root. Standard (non-daemon) mode expects to be communicating with
|
||||
the daemon mode over dbus.
|
||||
Running the program as a daemon manually will require root. Standard (non-daemon)
|
||||
mode expects to be communicating with the daemon mode over dbus.
|
||||
|
||||
Commands are given by:
|
||||
|
||||
@@ -182,60 +198,6 @@ Some commands may have subcommands:
|
||||
asusctl <command> <subcommand> --help
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
$ asusctl --help
|
||||
Usage: asusctl [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-v, --version show program version number
|
||||
-k, --kbd-bright VAL <off, low, med, high>
|
||||
-p, --pwr-profile PWR <silent, normal, boost>
|
||||
-c, --chg-limit CHRG <20-100>
|
||||
|
||||
Available commands:
|
||||
led-mode Set the keyboard lighting from built-in modes
|
||||
|
||||
$ asusctl led-mode --help
|
||||
Usage: asusctl led-mode [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
|
||||
Available commands:
|
||||
static set a single static colour
|
||||
breathe pulse between one or two colours
|
||||
strobe strobe through all colours
|
||||
rainbow rainbow cycling in one of four directions
|
||||
star rain pattern mimicking raindrops
|
||||
rain rain pattern of three preset colours
|
||||
highlight pressed keys are highlighted to fade
|
||||
laser pressed keys generate horizontal laser
|
||||
ripple pressed keys ripple outwards like a splash
|
||||
pulse set a rapid pulse
|
||||
comet set a vertical line zooming from left
|
||||
flash set a wide vertical line zooming from left
|
||||
multi-static 4-zone multi-colour
|
||||
|
||||
$ asusctl led-mode static --help
|
||||
Usage: asusctl led-mode static [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-c HEX set the RGB value e.g, ff00ff
|
||||
|
||||
$ asusctl led-mode star --help
|
||||
Usage: asusctl led-mode star [OPTIONS]
|
||||
|
||||
Optional arguments:
|
||||
-h, --help print help message
|
||||
-c HEX set the first RGB value e.g, ff00ff
|
||||
-C HEX set the second RGB value e.g, ff00ff
|
||||
-s SPEED set the speed: low, med, high
|
||||
```
|
||||
|
||||
## Daemon mode
|
||||
|
||||
If the daemon service is enabled then on boot the following will be reloaded from save:
|
||||
@@ -249,27 +211,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)
|
||||
|
||||
|
||||
101
README_DBUS.md
@@ -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,7 @@ 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:
|
||||
|
||||
```
|
||||
{
|
||||
"Static": {
|
||||
"colour": [ 255, 0, 0]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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 +105,5 @@ Monitoring dbus while sending commands via `rog-core` will give you the json str
|
||||
## Getting an introspection .xml
|
||||
|
||||
```
|
||||
dbus-send --system --print-reply --dest=org.asuslinux.Daemon /org/asuslinux/Daemon org.freedesktop.DBus.Introspectable.Introspect > xml/dbus-0.14.4.xml
|
||||
dbus-send --system --print-reply --dest=org.asuslinux.Daemon /org/asuslinux/Charge org.freedesktop.DBus.Introspectable.Introspect > xml/asusd-charge.xml
|
||||
```
|
||||
7
TODO.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# TODO
|
||||
|
||||
- There is lots of code duplication. This should be turned in to macros (dbus stuff etc)
|
||||
- Add a little more information to profile notifications such as freq min/max, fan curves
|
||||
- Finish splitting out controllers to own crates
|
||||
- Finish move to zbus in client when zbus has client signal watch
|
||||
- Consider a rename again because the project is getting a lot less ASUS centric
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "asus-nb-ctrl"
|
||||
version = "1.0.3"
|
||||
version = "2.0.3"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -22,10 +22,10 @@ name = "asusd"
|
||||
path = "src/daemon.rs"
|
||||
|
||||
[dependencies]
|
||||
ctrl-gfx = { path = "../ctrl-gfx" }
|
||||
asus-nb = { path = "../asus-nb" }
|
||||
rusb = "^0.6.0"
|
||||
udev = "^0.4.0"
|
||||
async-trait = "0.1.36"
|
||||
|
||||
# cli and logging
|
||||
gumdrop = "^0.8.0"
|
||||
@@ -33,9 +33,9 @@ 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.1.1"
|
||||
zvariant = "2.2.0"
|
||||
#tokio = { version = "^0.2.4", features = ["rt-threaded", "sync"] }
|
||||
|
||||
# serialisation
|
||||
serde = "^1.0"
|
||||
@@ -45,5 +45,8 @@ toml = "0.4.6"
|
||||
|
||||
# Device control
|
||||
sysfs-class = "^0.1.2" # used for backlight control and baord ID
|
||||
rog_fan_curve = { version = "0.1.5", features = ["serde"] }
|
||||
# cpu power management
|
||||
intel-pstate = "^0.2.1"
|
||||
intel-pstate = "^0.2.1"
|
||||
|
||||
yansi-term = "^0.1"
|
||||
@@ -1,6 +1,8 @@
|
||||
use asus_nb::aura_modes::AuraModes;
|
||||
use log::{error, warn};
|
||||
use rog_fan_curve::Curve;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
@@ -8,12 +10,16 @@ pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub gfx_managed: bool,
|
||||
pub active_profile: String,
|
||||
pub toggle_profiles: Vec<String>,
|
||||
// TODO: remove power_profile
|
||||
pub power_profile: u8,
|
||||
pub bat_charge_limit: u8,
|
||||
pub kbd_led_brightness: u8,
|
||||
pub kbd_backlight_mode: u8,
|
||||
pub kbd_backlight_modes: Vec<AuraModes>,
|
||||
pub power_profiles: FanModeProfile,
|
||||
pub power_profiles: BTreeMap<String, Profile>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -32,7 +38,10 @@ impl Config {
|
||||
self = Config::create_default(&mut file, &supported_led_modes);
|
||||
} else {
|
||||
self = serde_json::from_str(&buf).unwrap_or_else(|_| {
|
||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
||||
warn!(
|
||||
"Could not deserialise {}. Overwriting with default",
|
||||
CONFIG_PATH
|
||||
);
|
||||
Config::create_default(&mut file, &supported_led_modes)
|
||||
});
|
||||
}
|
||||
@@ -42,20 +51,38 @@ impl Config {
|
||||
|
||||
fn create_default(file: &mut File, supported_led_modes: &[u8]) -> Self {
|
||||
// create a default config here
|
||||
let mut c = Config::default();
|
||||
c.bat_charge_limit = 100;
|
||||
c.kbd_backlight_mode = 0;
|
||||
c.kbd_led_brightness = 1;
|
||||
let mut config = Config::default();
|
||||
config.gfx_managed = true;
|
||||
|
||||
config.bat_charge_limit = 100;
|
||||
config.kbd_backlight_mode = 0;
|
||||
config.kbd_led_brightness = 1;
|
||||
|
||||
for n in supported_led_modes {
|
||||
c.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
config.kbd_backlight_modes.push(AuraModes::from(*n))
|
||||
}
|
||||
|
||||
let profile = Profile::default();
|
||||
config.power_profiles.insert("normal".into(), profile);
|
||||
|
||||
let mut profile = Profile::default();
|
||||
profile.fan_preset = 1;
|
||||
config.power_profiles.insert("boost".into(), profile);
|
||||
|
||||
let mut profile = Profile::default();
|
||||
profile.fan_preset = 2;
|
||||
config.power_profiles.insert("silent".into(), profile);
|
||||
|
||||
config.toggle_profiles.push("normal".into());
|
||||
config.toggle_profiles.push("boost".into());
|
||||
config.toggle_profiles.push("silent".into());
|
||||
config.active_profile = "normal".into();
|
||||
|
||||
// Should be okay to unwrap this as is since it is a Default
|
||||
let json = serde_json::to_string_pretty(&c).unwrap();
|
||||
let json = serde_json::to_string_pretty(&config).unwrap();
|
||||
file.write_all(json.as_bytes())
|
||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
||||
c
|
||||
config
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
@@ -75,6 +102,17 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_new() -> Result<Config, Box<dyn std::error::Error>> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(&CONFIG_PATH)
|
||||
.unwrap_or_else(|err| panic!("Error reading {}: {}", CONFIG_PATH, err));
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)?;
|
||||
let x: Config = serde_json::from_str(&buf)?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
pub fn write(&self) {
|
||||
let mut file = File::create(CONFIG_PATH).expect("Couldn't overwrite config");
|
||||
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||
@@ -103,26 +141,26 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct FanModeProfile {
|
||||
pub normal: CPUSettings,
|
||||
pub boost: CPUSettings,
|
||||
pub silent: CPUSettings,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CPUSettings {
|
||||
pub struct Profile {
|
||||
pub min_percentage: u8,
|
||||
pub max_percentage: u8,
|
||||
pub no_turbo: bool,
|
||||
pub turbo: bool,
|
||||
pub fan_preset: u8,
|
||||
pub fan_curve: Option<Curve>,
|
||||
}
|
||||
|
||||
impl Default for CPUSettings {
|
||||
#[deprecated]
|
||||
pub type CPUSettings = Profile;
|
||||
|
||||
impl Default for Profile {
|
||||
fn default() -> Self {
|
||||
CPUSettings {
|
||||
Profile {
|
||||
min_percentage: 0,
|
||||
max_percentage: 100,
|
||||
no_turbo: false,
|
||||
turbo: false,
|
||||
fan_preset: 0,
|
||||
fan_curve: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,13 @@ const INIT: u8 = 0xc2;
|
||||
const APPLY: u8 = 0xc3;
|
||||
const SET: u8 = 0xc4;
|
||||
|
||||
use crate::config::Config;
|
||||
use asus_nb::error::AuraError;
|
||||
use log::{error, info, warn};
|
||||
use rusb::{Device, DeviceHandle};
|
||||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
@@ -34,32 +31,28 @@ 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>>;
|
||||
|
||||
/// 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));
|
||||
}
|
||||
})]
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
async fn reload_from_config(&mut self, _: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
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))
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,21 +60,18 @@ impl CtrlAnimeDisplay {
|
||||
#[inline]
|
||||
pub fn new() -> Result<CtrlAnimeDisplay, Box<dyn Error>> {
|
||||
// We don't expect this ID to ever change
|
||||
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b).map_err(|err| {
|
||||
warn!("Could not get AniMe display handle: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
let device = CtrlAnimeDisplay::get_device(0x0b05, 0x193b)?;
|
||||
|
||||
let mut device = device.open()?;
|
||||
device.reset()?;
|
||||
|
||||
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||
error!("Auto-detach kernel driver failed: {:?}", err);
|
||||
error!("Auto-detach kernel driver failed: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
device.claim_interface(0).map_err(|err| {
|
||||
error!("Could not claim device interface: {:?}", err);
|
||||
error!("Could not claim device interface: {}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
@@ -103,15 +93,15 @@ 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::WriteImage(effect) => self.write_image(effect)?,
|
||||
AnimatrixCommand::Set => self.do_set()?,
|
||||
AnimatrixCommand::Apply => self.do_apply()?,
|
||||
//AnimatrixCommand::ReloadLast => self.reload_last_builtin(&config).await?,
|
||||
}
|
||||
Ok(())
|
||||
@@ -119,8 +109,7 @@ impl CtrlAnimeDisplay {
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
#[inline]
|
||||
async fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
|
||||
let prev = std::time::Instant::now();
|
||||
fn write_bytes(&self, message: &[u8]) -> Result<(), AuraError> {
|
||||
match self.handle.write_control(
|
||||
0x21, // request_type
|
||||
0x09, // request
|
||||
@@ -129,15 +118,10 @@ impl CtrlAnimeDisplay {
|
||||
message,
|
||||
Duration::from_millis(200),
|
||||
) {
|
||||
Ok(_) => {
|
||||
println!(
|
||||
"{:?}",
|
||||
std::time::Instant::now().duration_since(prev).as_micros()
|
||||
);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(err) => match err {
|
||||
rusb::Error::Timeout => {}
|
||||
_ => error!("Failed to write to led interrupt: {:?}", err),
|
||||
_ => error!("Failed to write to led interrupt: {}", err),
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@@ -159,22 +143,22 @@ impl CtrlAnimeDisplay {
|
||||
///
|
||||
/// Where led brightness is 0..255, low to high
|
||||
#[inline]
|
||||
async fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||
fn write_image(&mut self, image: Vec<Vec<u8>>) -> Result<(), AuraError> {
|
||||
for row in image.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
self.do_flush().await?;
|
||||
self.do_flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_initialization(&mut self) -> Result<(), AuraError> {
|
||||
fn do_initialization(&mut self) -> Result<(), AuraError> {
|
||||
let mut init = [0; PACKET_SIZE];
|
||||
init[0] = DEV_PAGE; // This is the USB page we're using throughout
|
||||
for (idx, byte) in INIT_STR.as_bytes().iter().enumerate() {
|
||||
init[idx + 1] = *byte
|
||||
}
|
||||
self.write_bytes(&init).await?;
|
||||
self.write_bytes(&init)?;
|
||||
|
||||
// clear the init array and write other init message
|
||||
for ch in init.iter_mut() {
|
||||
@@ -183,43 +167,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(&mut self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = SET;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = 0x80;
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
self.write_bytes(&flush)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn do_apply(&mut self) -> Result<(), AuraError> {
|
||||
fn do_apply(&mut self) -> Result<(), AuraError> {
|
||||
let mut flush = [0; PACKET_SIZE];
|
||||
flush[0] = DEV_PAGE;
|
||||
flush[1] = APPLY;
|
||||
flush[2] = 0x01;
|
||||
flush[3] = 0x80;
|
||||
|
||||
self.write_bytes(&flush).await?;
|
||||
self.write_bytes(&flush)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,98 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use crate::{config::Config, error::RogError};
|
||||
//use crate::dbus::DbusEvents;
|
||||
use log::{info, warn};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
static BAT_CHARGE_PATH: &str = "/sys/class/power_supply/BAT0/charge_control_end_threshold";
|
||||
|
||||
pub struct CtrlCharge {
|
||||
path: &'static str,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
trait Dbus {
|
||||
fn set_limit(&mut self, charge: u8);
|
||||
fn limit(&self) -> i8;
|
||||
fn notify_charge(&self, limit: u8) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlCharge {
|
||||
type A = u8;
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
_: Option<Arc<SyncConnection>>,
|
||||
_: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
vec![tokio::spawn(async move {
|
||||
while let Some(n) = recv.recv().await {
|
||||
let mut config = config.lock().await;
|
||||
self.set_charge_limit(n, &mut config)
|
||||
.unwrap_or_else(|err| warn!("charge_limit: {:?}", err));
|
||||
}
|
||||
})]
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl Dbus for CtrlCharge {
|
||||
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)
|
||||
fn limit(&self) -> i8 {
|
||||
if let Ok(config) = self.config.try_lock() {
|
||||
return config.bat_charge_limit as i8;
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_charge(&self, limit: u8) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for CtrlCharge {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Charge".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlCharge: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlCharge {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
config.read();
|
||||
info!("Reloaded battery charge limit");
|
||||
self.set(config.bat_charge_limit, &mut config)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlCharge {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
let path = CtrlCharge::get_battery_path()?;
|
||||
info!("Device has battery charge threshold control");
|
||||
Ok(CtrlCharge { path })
|
||||
Ok(CtrlCharge { path, config })
|
||||
}
|
||||
|
||||
fn get_battery_path() -> Result<&'static str, std::io::Error> {
|
||||
fn get_battery_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(BAT_CHARGE_PATH).exists() {
|
||||
Ok(BAT_CHARGE_PATH)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"Charge control not available",
|
||||
Err(RogError::MissingFunction(
|
||||
"Charge control not available".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_charge_limit(
|
||||
&self,
|
||||
limit: u8,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
pub(super) fn set(&self, limit: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
if limit < 20 || limit > 100 {
|
||||
warn!(
|
||||
"Unable to set battery charge limit, must be between 20-100: requested {}",
|
||||
@@ -79,12 +103,9 @@ impl CtrlCharge {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to open battery charge limit path: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
file.write_all(limit.to_string().as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", BAT_CHARGE_PATH, err));
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
info!("Battery charge limit: {}", limit);
|
||||
|
||||
config.read();
|
||||
|
||||
@@ -1,232 +1,342 @@
|
||||
use crate::config::Config;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use crate::config::{Config, Profile};
|
||||
use asus_nb::profile::ProfileEvent;
|
||||
use log::{info, warn};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::Mutex;
|
||||
use zbus::dbus_interface;
|
||||
|
||||
static FAN_TYPE_1_PATH: &str = "/sys/devices/platform/asus-nb-wmi/throttle_thermal_policy";
|
||||
static FAN_TYPE_2_PATH: &str = "/sys/devices/platform/asus-nb-wmi/fan_boost_mode";
|
||||
static AMD_BOOST_PATH: &str = "/sys/devices/system/cpu/cpufreq/boost";
|
||||
|
||||
pub struct CtrlFanAndCPU {
|
||||
path: &'static str,
|
||||
pub path: &'static str,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
use async_trait::async_trait;
|
||||
pub struct DbusFanAndCpu {
|
||||
inner: Arc<Mutex<CtrlFanAndCPU>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlFanAndCPU {
|
||||
type A = u8;
|
||||
impl DbusFanAndCpu {
|
||||
pub fn new(inner: Arc<Mutex<CtrlFanAndCPU>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
_: Option<Arc<SyncConnection>>,
|
||||
_: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
let gate1 = Arc::new(Mutex::new(self));
|
||||
let gate2 = gate1.clone();
|
||||
let config1 = config.clone();
|
||||
// spawn an endless loop
|
||||
vec![
|
||||
tokio::spawn(async move {
|
||||
while let Some(mode) = recv.recv().await {
|
||||
let mut config = config1.lock().await;
|
||||
let mut lock = gate1.lock().await;
|
||||
lock.set_fan_mode(mode, &mut config)
|
||||
.unwrap_or_else(|err| warn!("{:?}", err));
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl DbusFanAndCpu {
|
||||
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));
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(config.power_profile), config)?;
|
||||
info!(
|
||||
"Reloaded fan mode: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
fn profile(&mut self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
||||
cfg.read();
|
||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
||||
if let Ok(json) = serde_json::to_string(profile) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"Failed".to_string()
|
||||
}
|
||||
|
||||
fn profiles(&mut self) -> String {
|
||||
if let Ok(ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.try_lock() {
|
||||
cfg.read();
|
||||
if let Ok(json) = serde_json::to_string(&cfg.power_profiles) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
||||
"Failed".to_string()
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_profile(&self, profile: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl crate::ZbusAdd for DbusFanAndCpu {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Profile".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("DbusFanAndCpu: {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Reloadable for CtrlFanAndCPU {
|
||||
fn reload(&mut self) -> Result<(), RogError> {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
file.write_all(format!("{}\n", config.power_profile).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
let profile = config.active_profile.clone();
|
||||
self.set(&profile, &mut config)?;
|
||||
info!(
|
||||
"Reloaded fan mode: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CtrlTask for CtrlFanAndCPU {
|
||||
fn do_task(&mut self) -> Result<(), RogError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read(self.path.into(), err))?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
if config.power_profile != num as u8 {
|
||||
config.read();
|
||||
|
||||
let mut i = config
|
||||
.toggle_profiles
|
||||
.iter()
|
||||
.position(|x| x == &config.active_profile)
|
||||
.map(|i| i + 1)
|
||||
.unwrap_or(0);
|
||||
if i >= config.toggle_profiles.len() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
let new_profile = config
|
||||
.toggle_profiles
|
||||
.get(i)
|
||||
.unwrap_or(&config.active_profile)
|
||||
.clone();
|
||||
|
||||
self.set(&new_profile, &mut config)?;
|
||||
|
||||
info!("Profile was changed: {}", &new_profile);
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(RogError::DoTask("Fan-level could not be parsed".into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlFanAndCPU {
|
||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||
pub fn new(config: Arc<Mutex<Config>>) -> Result<Self, RogError> {
|
||||
let path = CtrlFanAndCPU::get_fan_path()?;
|
||||
info!("Device has thermal throttle control");
|
||||
Ok(CtrlFanAndCPU { path })
|
||||
Ok(CtrlFanAndCPU { path, config })
|
||||
}
|
||||
|
||||
fn get_fan_path() -> Result<&'static str, std::io::Error> {
|
||||
fn get_fan_path() -> Result<&'static str, RogError> {
|
||||
if Path::new(FAN_TYPE_1_PATH).exists() {
|
||||
Ok(FAN_TYPE_1_PATH)
|
||||
} else if Path::new(FAN_TYPE_2_PATH).exists() {
|
||||
Ok(FAN_TYPE_2_PATH)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"Fan mode not available",
|
||||
Err(RogError::MissingFunction(
|
||||
"Fan mode not available, you may require a v5.8 series kernel or newer".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn fan_mode_check_change(
|
||||
&mut self,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = OpenOptions::new().read(true).open(self.path)?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if config.power_profile != num as u8 {
|
||||
config.read();
|
||||
config.power_profile = num as u8;
|
||||
config.write();
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(config.power_profile), config)?;
|
||||
info!(
|
||||
"Fan mode was changed: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Fan-level could not be parsed",
|
||||
);
|
||||
Err(Box::new(err))
|
||||
}
|
||||
|
||||
pub(super) fn set_fan_mode(
|
||||
&mut self,
|
||||
n: u8,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut fan_ctrl = OpenOptions::new().write(true).open(self.path)?;
|
||||
pub(super) fn do_update(&mut self, config: &mut Config) -> Result<(), RogError> {
|
||||
config.read();
|
||||
config.power_profile = n;
|
||||
config.write();
|
||||
fan_ctrl
|
||||
.write_all(format!("{:?}\n", config.power_profile).as_bytes())
|
||||
.unwrap_or_else(|err| error!("Could not write to {}, {:?}", self.path, err));
|
||||
info!(
|
||||
"Fan mode set to: {:?}",
|
||||
FanLevel::from(config.power_profile)
|
||||
);
|
||||
self.set_pstate_for_fan_mode(FanLevel::from(n), config)?;
|
||||
|
||||
let mut i = config
|
||||
.toggle_profiles
|
||||
.iter()
|
||||
.position(|x| x == &config.active_profile)
|
||||
.map(|i| i + 1)
|
||||
.unwrap_or(0);
|
||||
if i >= config.toggle_profiles.len() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
let new_profile = config
|
||||
.toggle_profiles
|
||||
.get(i)
|
||||
.unwrap_or(&config.active_profile)
|
||||
.clone();
|
||||
|
||||
self.set(&new_profile, config)?;
|
||||
|
||||
info!("Profile was changed: {}", &new_profile);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pstate_for_fan_mode(
|
||||
&self,
|
||||
mode: FanLevel,
|
||||
pub(super) fn set_fan_mode(&mut self, preset: u8, config: &mut Config) -> Result<(), RogError> {
|
||||
let mode = config.active_profile.clone();
|
||||
let mut fan_ctrl = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
config.read();
|
||||
let mut mode_config = config
|
||||
.power_profiles
|
||||
.get_mut(&mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.clone()))?;
|
||||
config.power_profile = preset;
|
||||
mode_config.fan_preset = preset;
|
||||
config.write();
|
||||
fan_ctrl
|
||||
.write_all(format!("{}\n", preset).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
info!("Fan mode set to: {:?}", FanLevel::from(preset));
|
||||
self.set_pstate_for_fan_mode(&mode, config)?;
|
||||
self.set_fan_curve_for_fan_mode(&mode, config)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_profile_event(
|
||||
&mut self,
|
||||
event: &ProfileEvent,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
) -> Result<(), RogError> {
|
||||
match event {
|
||||
ProfileEvent::Toggle => self.do_update(config)?,
|
||||
ProfileEvent::ChangeMode(mode) => {
|
||||
self.set_fan_mode(*mode, config)?;
|
||||
}
|
||||
ProfileEvent::Cli(command) => {
|
||||
let profile_key = match command.profile.as_ref() {
|
||||
Some(k) => k.clone(),
|
||||
None => config.active_profile.clone(),
|
||||
};
|
||||
|
||||
let mut profile = if command.create {
|
||||
config
|
||||
.power_profiles
|
||||
.entry(profile_key.clone())
|
||||
.or_insert_with(Profile::default)
|
||||
} else {
|
||||
config
|
||||
.power_profiles
|
||||
.get_mut(&profile_key)
|
||||
.ok_or_else(|| RogError::MissingProfile(profile_key.clone()))?
|
||||
};
|
||||
|
||||
if command.turbo.is_some() {
|
||||
profile.turbo = command.turbo.unwrap();
|
||||
}
|
||||
if let Some(min_perc) = command.min_percentage {
|
||||
profile.min_percentage = min_perc;
|
||||
}
|
||||
if let Some(max_perc) = command.max_percentage {
|
||||
profile.max_percentage = max_perc;
|
||||
}
|
||||
if let Some(ref preset) = command.preset {
|
||||
profile.fan_preset = preset.into();
|
||||
}
|
||||
if let Some(ref curve) = command.curve {
|
||||
profile.fan_curve = Some(curve.clone());
|
||||
}
|
||||
|
||||
self.set(&profile_key, config)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set(&mut self, profile: &str, config: &mut Config) -> Result<(), RogError> {
|
||||
let mode_config = config
|
||||
.power_profiles
|
||||
.get(profile)
|
||||
.ok_or_else(|| RogError::MissingProfile(profile.into()))?;
|
||||
let mut fan_ctrl = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(self.path)
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
fan_ctrl
|
||||
.write_all(format!("{}\n", mode_config.fan_preset).as_bytes())
|
||||
.map_err(|err| RogError::Write(self.path.into(), err))?;
|
||||
config.power_profile = mode_config.fan_preset;
|
||||
|
||||
self.set_pstate_for_fan_mode(profile, config)?;
|
||||
self.set_fan_curve_for_fan_mode(profile, config)?;
|
||||
|
||||
config.active_profile = profile.into();
|
||||
|
||||
config.write();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pstate_for_fan_mode(&self, mode: &str, config: &mut Config) -> Result<(), RogError> {
|
||||
info!("Setting pstate");
|
||||
let mode_config = config
|
||||
.power_profiles
|
||||
.get(mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.into()))?;
|
||||
|
||||
// Set CPU pstate
|
||||
if let Ok(pstate) = intel_pstate::PState::new() {
|
||||
match mode {
|
||||
FanLevel::Normal => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.normal.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.normal.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.normal.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.normal.min_percentage,
|
||||
config.power_profiles.normal.max_percentage,
|
||||
!config.power_profiles.normal.no_turbo
|
||||
);
|
||||
}
|
||||
FanLevel::Boost => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.boost.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.boost.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.boost.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.boost.min_percentage,
|
||||
config.power_profiles.boost.max_percentage,
|
||||
!config.power_profiles.boost.no_turbo
|
||||
);
|
||||
}
|
||||
FanLevel::Silent => {
|
||||
pstate.set_min_perf_pct(config.power_profiles.silent.min_percentage)?;
|
||||
pstate.set_max_perf_pct(config.power_profiles.silent.max_percentage)?;
|
||||
pstate.set_no_turbo(config.power_profiles.silent.no_turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {:?}%, max: {:?}%, turbo: {:?}",
|
||||
config.power_profiles.silent.min_percentage,
|
||||
config.power_profiles.silent.max_percentage,
|
||||
!config.power_profiles.silent.no_turbo
|
||||
);
|
||||
}
|
||||
}
|
||||
pstate.set_min_perf_pct(mode_config.min_percentage)?;
|
||||
pstate.set_max_perf_pct(mode_config.max_percentage)?;
|
||||
pstate.set_no_turbo(!mode_config.turbo)?;
|
||||
info!(
|
||||
"Intel CPU Power: min: {}%, max: {}%, turbo: {}",
|
||||
mode_config.min_percentage, mode_config.max_percentage, mode_config.turbo
|
||||
);
|
||||
} else {
|
||||
info!("Setting pstate for AMD CPU");
|
||||
// must be AMD CPU
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open(AMD_BOOST_PATH)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to open AMD boost: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
match mode {
|
||||
FanLevel::Normal => {
|
||||
let boost = if config.power_profiles.normal.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
}; // opposite of Intel
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
FanLevel::Boost => {
|
||||
let boost = if config.power_profiles.boost.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
};
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
FanLevel::Silent => {
|
||||
let boost = if config.power_profiles.silent.no_turbo {
|
||||
"0"
|
||||
} else {
|
||||
"1"
|
||||
};
|
||||
file.write_all(boost.as_bytes()).unwrap_or_else(|err| {
|
||||
error!("Could not write to {}, {:?}", AMD_BOOST_PATH, err)
|
||||
});
|
||||
info!("AMD CPU Turbo: {:?}", boost);
|
||||
}
|
||||
.map_err(|err| RogError::Path(self.path.into(), err))?;
|
||||
|
||||
let boost = if mode_config.turbo { "1" } else { "0" }; // opposite of Intel
|
||||
file.write_all(boost.as_bytes())
|
||||
.map_err(|err| RogError::Write(AMD_BOOST_PATH.into(), err))?;
|
||||
info!("AMD CPU Turbo: {}", boost);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_fan_curve_for_fan_mode(&self, mode: &str, config: &Config) -> Result<(), RogError> {
|
||||
let mode_config = &config
|
||||
.power_profiles
|
||||
.get(mode)
|
||||
.ok_or_else(|| RogError::MissingProfile(mode.into()))?;
|
||||
|
||||
if let Some(ref curve) = mode_config.fan_curve {
|
||||
use rog_fan_curve::{Board, Fan};
|
||||
if let Some(board) = Board::from_board_name() {
|
||||
curve.apply(board, Fan::Cpu)?;
|
||||
curve.apply(board, Fan::Gpu)?;
|
||||
} else {
|
||||
warn!("Fan curve unsupported on this board.")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,175 +2,243 @@
|
||||
static LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
static LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
use crate::{config::Config, error::RogError};
|
||||
use asus_nb::{
|
||||
aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, DBUS_IFACE, DBUS_PATH,
|
||||
LED_MSG_LEN,
|
||||
};
|
||||
use dbus::{channel::Sender, nonblock::SyncConnection, tree::Signal};
|
||||
use crate::{config::Config, error::RogError, laptops::HELP_ADDRESS};
|
||||
use asus_nb::{aura_brightness_bytes, aura_modes::AuraModes, fancy::KeyColourArray, LED_MSG_LEN};
|
||||
use log::{info, warn};
|
||||
use std::error::Error;
|
||||
use std::convert::TryInto;
|
||||
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 zbus::dbus_interface;
|
||||
|
||||
pub struct CtrlKbdBacklight {
|
||||
led_node: String,
|
||||
kbd_node: String,
|
||||
bright_node: String,
|
||||
led_node: Option<String>,
|
||||
#[allow(dead_code)]
|
||||
kbd_node: Option<String>,
|
||||
pub bright_node: String,
|
||||
supported_modes: Vec<u8>,
|
||||
flip_effect_write: bool,
|
||||
config: Arc<Mutex<Config>>,
|
||||
}
|
||||
|
||||
use async_trait::async_trait;
|
||||
pub struct DbusKbdBacklight {
|
||||
inner: Arc<Mutex<CtrlKbdBacklight>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::Controller for CtrlKbdBacklight {
|
||||
type A = AuraModes;
|
||||
impl DbusKbdBacklight {
|
||||
pub fn new(inner: Arc<Mutex<CtrlKbdBacklight>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns two tasks which continuously check for changes
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
mut recv: Receiver<Self::A>,
|
||||
connection: Option<Arc<SyncConnection>>,
|
||||
signal: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>> {
|
||||
let gate1 = Arc::new(Mutex::new(self));
|
||||
let gate2 = gate1.clone();
|
||||
trait Dbus {
|
||||
fn set_led(&mut self, data: String);
|
||||
fn ledmode(&self) -> String;
|
||||
fn notify_led(&self, data: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
let config1 = config.clone();
|
||||
vec![
|
||||
tokio::spawn(async move {
|
||||
while let Some(command) = recv.recv().await {
|
||||
let mut config = config1.lock().await;
|
||||
let mut lock = gate1.lock().await;
|
||||
match &command {
|
||||
impl crate::ZbusAdd for DbusKbdBacklight {
|
||||
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Led".try_into().unwrap(), self)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl DbusKbdBacklight {
|
||||
fn set_led_mode(&mut self, data: String) {
|
||||
if let Ok(data) = serde_json::from_str(&data) {
|
||||
if let Ok(mut ctrl) = self.inner.try_lock() {
|
||||
if let Ok(mut cfg) = ctrl.config.clone().try_lock() {
|
||||
match &data {
|
||||
AuraModes::PerKey(_) => {
|
||||
lock.do_command(command, &mut config)
|
||||
.await
|
||||
ctrl.do_command(data, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
_ => {
|
||||
let json = serde_json::to_string(&command).unwrap();
|
||||
lock.do_command(command, &mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
connection
|
||||
.as_ref()
|
||||
.expect("LED Controller must have DBUS connection")
|
||||
.send(
|
||||
signal
|
||||
.as_ref()
|
||||
.expect("LED Controller must have DBUS signal")
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(json),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
if let Ok(json) = serde_json::to_string(&data) {
|
||||
ctrl.do_command(data, &mut cfg)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_led(&json).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::delay_for(std::time::Duration::from_millis(100)).await;
|
||||
let mut lock = gate2.lock().await;
|
||||
let mut config = config.lock().await;
|
||||
lock.let_bright_check_change(&mut config)
|
||||
.unwrap_or_else(|err| warn!("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 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;
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
"SetKeyBacklight could not deserialise".to_string()
|
||||
}
|
||||
|
||||
// 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 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()
|
||||
}
|
||||
|
||||
#[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| RogError::Path((&self.bright_node).into(), err))?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)
|
||||
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if let Ok(mut config) = self.config.clone().try_lock() {
|
||||
if config.kbd_led_brightness != num as u8 {
|
||||
config.read();
|
||||
config.kbd_led_brightness = num as u8;
|
||||
config.write();
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Err(RogError::ParseLED)
|
||||
}
|
||||
}
|
||||
|
||||
impl CtrlKbdBacklight {
|
||||
#[inline]
|
||||
pub fn new(id_product: &str, supported_modes: Vec<u8>) -> Result<Self, std::io::Error> {
|
||||
Ok(CtrlKbdBacklight {
|
||||
led_node: Self::get_node_failover(id_product, Self::scan_led_node)?,
|
||||
kbd_node: Self::get_node_failover(id_product, Self::scan_kbd_node)?,
|
||||
// brightness node path should always be constant but this *might* change?
|
||||
pub fn new(
|
||||
id_product: &str,
|
||||
condev_iface: Option<&String>,
|
||||
supported_modes: Vec<u8>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
) -> Self {
|
||||
// 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(),
|
||||
// TODO: Check for existance
|
||||
bright_node: "/sys/class/leds/asus::kbd_backlight/brightness".to_string(),
|
||||
supported_modes,
|
||||
flip_effect_write: false,
|
||||
})
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_node_failover(id_product: &str, fun: fn(&str) -> Result<String, std::io::Error>) -> Result<String, std::io::Error> {
|
||||
for n in 0..2 {
|
||||
match fun(id_product) {
|
||||
fn get_node_failover(
|
||||
id_product: &str,
|
||||
iface: Option<&String>,
|
||||
fun: fn(&str, Option<&String>) -> Result<String, RogError>,
|
||||
) -> Result<String, RogError> {
|
||||
for n in 0..=2 {
|
||||
// 0,1,2 inclusive
|
||||
match fun(id_product, iface) {
|
||||
Ok(o) => return Ok(o),
|
||||
Err(e) => {
|
||||
if n > 0 {
|
||||
if n == 2 {
|
||||
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) -> Result<String, std::io::Error> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
enumerator.match_subsystem("hidraw")?;
|
||||
fn scan_led_node(id_product: &str, _: Option<&String>) -> Result<String, RogError> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices()? {
|
||||
if let Some(parent) = device.parent_with_subsystem_devtype("usb", "usb_device")? {
|
||||
if parent.attribute_value("idProduct").unwrap() == id_product {
|
||||
// && device.parent().unwrap().sysnum().unwrap() == 3
|
||||
for device in enumerator.scan_devices().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("scan_devices failed".into(), err)
|
||||
})? {
|
||||
if let Some(parent) = device
|
||||
.parent_with_subsystem_devtype("usb", "usb_device")
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
|
||||
})?
|
||||
{
|
||||
if parent
|
||||
.attribute_value("idProduct")
|
||||
.ok_or(RogError::NotFound("LED idProduct".into()))?
|
||||
== id_product
|
||||
{
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
info!("Using device at: {:?} for LED control", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
@@ -178,82 +246,82 @@ impl CtrlKbdBacklight {
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS LED device node not found",
|
||||
);
|
||||
Err(err)
|
||||
warn!("Did not find a hidraw node for LED control, your device may be unsupported or require a kernel patch, see: {}", HELP_ADDRESS);
|
||||
Err(RogError::MissingFunction(
|
||||
"ASUS LED device node not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_kbd_node(id_product: &str) -> Result<String, std::io::Error> {
|
||||
let mut enumerator = udev::Enumerator::new()?;
|
||||
enumerator.match_subsystem("input")?;
|
||||
enumerator.match_property("ID_MODEL_ID", id_product)?;
|
||||
fn scan_kbd_node(id_product: &str, iface: Option<&String>) -> Result<String, RogError> {
|
||||
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("enumerator failed".into(), err)
|
||||
})?;
|
||||
enumerator.match_subsystem("input").map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_subsystem failed".into(), err)
|
||||
})?;
|
||||
enumerator
|
||||
.match_property("ID_MODEL_ID", id_product)
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("match_property failed".into(), err)
|
||||
})?;
|
||||
|
||||
for device in enumerator.scan_devices()? {
|
||||
for device in enumerator
|
||||
.scan_devices()
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
err
|
||||
})
|
||||
.map_err(|err| {
|
||||
warn!("{}", err);
|
||||
RogError::Udev("scan_devices failed".into(), err)
|
||||
})?
|
||||
{
|
||||
if let Some(dev_node) = device.devnode() {
|
||||
if let Some(inum) = device.property_value("ID_USB_INTERFACE_NUM") {
|
||||
if inum == "02" {
|
||||
info!("Using device at: {:?} for keyboard polling", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
if let Some(iface) = iface {
|
||||
if inum == iface.as_str() {
|
||||
info!("Using device at: {:?} for keyboard polling", dev_node);
|
||||
return Ok(dev_node.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"ASUS N-Key Consumer Device node not found",
|
||||
);
|
||||
Err(err)
|
||||
|
||||
warn!("Did not find keyboard consumer device node, if expected functions are missing please file an issue at {}", HELP_ADDRESS);
|
||||
Err(RogError::MissingFunction(
|
||||
"ASUS keyboard 'Consumer Device' node not found".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn let_bright_check_change(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = OpenOptions::new().read(true).open(&self.bright_node)?;
|
||||
let mut buf = [0u8; 1];
|
||||
file.read_exact(&mut buf)?;
|
||||
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||
if config.kbd_led_brightness != num as u8 {
|
||||
config.read();
|
||||
config.kbd_led_brightness = num as u8;
|
||||
config.write();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let err = std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"LED brightness could not be parsed",
|
||||
);
|
||||
Err(Box::new(err))
|
||||
}
|
||||
|
||||
pub async fn do_command(
|
||||
&mut self,
|
||||
mode: AuraModes,
|
||||
config: &mut Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
self.set_and_save(mode, config).await
|
||||
pub fn do_command(&mut self, mode: AuraModes, config: &mut Config) -> Result<(), RogError> {
|
||||
self.set_and_save(mode, config)
|
||||
}
|
||||
|
||||
/// Should only be used if the bytes you are writing are verified correct
|
||||
#[inline]
|
||||
async fn write_bytes(&self, message: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||
if let Ok(mut file) = OpenOptions::new().write(true).open(&self.led_node) {
|
||||
file.write_all(message).unwrap();
|
||||
return Ok(());
|
||||
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
if let Some(led_node) = &self.led_node {
|
||||
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
|
||||
return file.write_all(message).map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||
}
|
||||
}
|
||||
Err(Box::new(RogError::NotSupported))
|
||||
Err(RogError::NotSupported)
|
||||
}
|
||||
|
||||
/// Write an effect block
|
||||
#[inline]
|
||||
async fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), Box<dyn Error>> {
|
||||
fn write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
||||
if self.flip_effect_write {
|
||||
for row in effect.iter().rev() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
} else {
|
||||
for row in effect.iter() {
|
||||
self.write_bytes(row).await?;
|
||||
self.write_bytes(row)?;
|
||||
}
|
||||
}
|
||||
self.flip_effect_write = !self.flip_effect_write;
|
||||
@@ -264,15 +332,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();
|
||||
@@ -281,15 +345,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();
|
||||
@@ -299,14 +363,14 @@ impl CtrlKbdBacklight {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn write_mode(&mut self, mode: &AuraModes) -> Result<(), Box<dyn Error>> {
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -316,20 +380,20 @@ impl CtrlKbdBacklight {
|
||||
if self.supported_modes.contains(&mode_num) {
|
||||
let bytes: [[u8; LED_MSG_LEN]; 4] = mode.into();
|
||||
for array in bytes.iter() {
|
||||
self.write_bytes(array).await?;
|
||||
self.write_bytes(array)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if self.supported_modes.contains(&mode_num) {
|
||||
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||
self.write_bytes(&bytes).await?;
|
||||
self.write_bytes(&bytes)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.write_bytes(&LED_SET).await?;
|
||||
self.write_bytes(&LED_SET)?;
|
||||
// Changes won't persist unless apply is set
|
||||
self.write_bytes(&LED_APPLY).await?;
|
||||
self.write_bytes(&LED_APPLY)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
use daemon::{
|
||||
config::Config, ctrl_anime::CtrlAnimeDisplay, ctrl_charge::CtrlCharge,
|
||||
ctrl_fan_cpu::CtrlFanAndCPU, ctrl_leds::CtrlKbdBacklight, dbus::dbus_create_tree,
|
||||
laptops::match_laptop,
|
||||
};
|
||||
use ctrl_gfx::ctrl_gfx::CtrlGraphics;
|
||||
use daemon::config::Config;
|
||||
use daemon::ctrl_anime::CtrlAnimeDisplay;
|
||||
use daemon::ctrl_charge::CtrlCharge;
|
||||
use daemon::ctrl_fan_cpu::{CtrlFanAndCPU, DbusFanAndCpu};
|
||||
use daemon::ctrl_leds::{CtrlKbdBacklight, DbusKbdBacklight};
|
||||
use daemon::laptops::match_laptop;
|
||||
|
||||
use dbus::{
|
||||
channel::Sender,
|
||||
nonblock::{Process, SyncConnection},
|
||||
tree::Signal,
|
||||
};
|
||||
use dbus_tokio::connection;
|
||||
|
||||
use asus_nb::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||
use daemon::Controller;
|
||||
use asus_nb::DBUS_NAME;
|
||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||
use log::LevelFilter;
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use 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 +27,7 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.init();
|
||||
|
||||
info!("Version: {}", daemon::VERSION);
|
||||
start_daemon().await?;
|
||||
start_daemon()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -43,211 +40,100 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// as fast as 1ms per row of the matrix inside it. (10ms total time)
|
||||
//
|
||||
// DBUS processing takes 6ms if not tokiod
|
||||
pub async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
let laptop = match_laptop();
|
||||
let mut config = if let Some(laptop) = laptop.as_ref() {
|
||||
let config = if let Some(laptop) = laptop.as_ref() {
|
||||
Config::default().load(laptop.supported_modes())
|
||||
} else {
|
||||
Config::default().load(&[])
|
||||
};
|
||||
|
||||
let mut led_control = if let Some(laptop) = laptop {
|
||||
CtrlKbdBacklight::new(laptop.usb_product(), laptop.supported_modes().to_owned())
|
||||
.map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut charge_control = CtrlCharge::new().map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
let mut fan_control = CtrlFanAndCPU::new().map_or_else(
|
||||
|err| {
|
||||
error!("{}", err);
|
||||
None
|
||||
},
|
||||
Some,
|
||||
);
|
||||
|
||||
// Reload settings
|
||||
if let Some(ctrl) = fan_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Fan mode: {}", err));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = charge_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Battery charge limit: {}", err));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = led_control.as_mut() {
|
||||
ctrl.reload_from_config(&mut config)
|
||||
.await
|
||||
.unwrap_or_else(|err| warn!("Reload settings: {}", err));
|
||||
}
|
||||
|
||||
let (resource, connection) = connection::new_system_sync()?;
|
||||
tokio::spawn(async {
|
||||
let err = resource.await;
|
||||
panic!("Lost connection to D-Bus: {}", err);
|
||||
});
|
||||
|
||||
connection
|
||||
.request_name(DBUS_NAME, false, true, true)
|
||||
.await?;
|
||||
let connection = Connection::new_system()?;
|
||||
fdo::DBusProxy::new(&connection)?
|
||||
.request_name(DBUS_NAME, fdo::RequestNameFlags::ReplaceExisting.into())?;
|
||||
let mut object_server = zbus::ObjectServer::new(&connection);
|
||||
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
let (
|
||||
tree,
|
||||
aura_command_recv,
|
||||
animatrix_recv,
|
||||
fan_mode_recv,
|
||||
charge_limit_recv,
|
||||
led_changed_signal,
|
||||
fanmode_signal,
|
||||
charge_limit_signal,
|
||||
) = dbus_create_tree(config.clone());
|
||||
|
||||
// We add the tree to the connection so that incoming method calls will be handled.
|
||||
tree.start_receive_send(&*connection);
|
||||
|
||||
// Send boot signals
|
||||
send_boot_signals(
|
||||
connection.clone(),
|
||||
config.clone(),
|
||||
fanmode_signal.clone(),
|
||||
charge_limit_signal.clone(),
|
||||
led_changed_signal.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// For helping with processing signals
|
||||
start_signal_task(
|
||||
connection.clone(),
|
||||
config.clone(),
|
||||
fanmode_signal,
|
||||
charge_limit_signal,
|
||||
);
|
||||
|
||||
// Begin all tasks
|
||||
let mut handles = Vec::new();
|
||||
if let Ok(ctrl) = CtrlAnimeDisplay::new() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), animatrix_recv, None, None));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = fan_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), fan_mode_recv, None, None));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = charge_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(config.clone(), charge_limit_recv, None, None));
|
||||
}
|
||||
|
||||
if let Some(ctrl) = led_control.take() {
|
||||
handles.append(&mut ctrl.spawn_task_loop(
|
||||
config.clone(),
|
||||
aura_command_recv,
|
||||
Some(connection.clone()),
|
||||
Some(led_changed_signal),
|
||||
));
|
||||
}
|
||||
|
||||
connection.process_all();
|
||||
for handle in handles {
|
||||
handle.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Move these in to the controllers tasks
|
||||
fn start_signal_task(
|
||||
connection: Arc<SyncConnection>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
fanmode_signal: Arc<Signal<()>>,
|
||||
charge_limit_signal: Arc<Signal<()>>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
// Some small things we need to track, without passing all sorts of stuff around
|
||||
let mut last_fan_mode = config.lock().await.power_profile;
|
||||
let mut last_charge_limit = config.lock().await.bat_charge_limit;
|
||||
loop {
|
||||
// Use tokio sleep to not hold up other threads
|
||||
tokio::time::delay_for(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
let config = config.lock().await;
|
||||
if config.power_profile != last_fan_mode {
|
||||
last_fan_mode = config.power_profile;
|
||||
connection
|
||||
.send(
|
||||
fanmode_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(last_fan_mode),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
}
|
||||
|
||||
if config.bat_charge_limit != last_charge_limit {
|
||||
last_charge_limit = config.bat_charge_limit;
|
||||
connection
|
||||
.send(
|
||||
charge_limit_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(last_charge_limit),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
match CtrlAnimeDisplay::new() {
|
||||
Ok(ctrl) => {
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("AniMe control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
connection
|
||||
.send(
|
||||
charge_limit_signal
|
||||
.msg(&DBUS_PATH.into(), &DBUS_IFACE.into())
|
||||
.append1(config.bat_charge_limit),
|
||||
)
|
||||
.unwrap_or_else(|_| 0);
|
||||
match CtrlGraphics::new() {
|
||||
Ok(mut ctrl) => {
|
||||
ctrl.reload()
|
||||
.unwrap_or_else(|err| warn!("Gfx controller: {}", err));
|
||||
ctrl.add_to_server(&mut object_server);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Gfx control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
// Collect tasks for task thread
|
||||
let mut tasks: Vec<Arc<Mutex<dyn CtrlTask + Send>>> = Vec::new();
|
||||
|
||||
match CtrlFanAndCPU::new(config.clone()) {
|
||||
Ok(mut ctrl) => {
|
||||
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);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Profile control: {}", err);
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(laptop) = laptop {
|
||||
let ctrl = CtrlKbdBacklight::new(
|
||||
laptop.usb_product(),
|
||||
laptop.condev_iface(),
|
||||
laptop.supported_modes().to_owned(),
|
||||
config,
|
||||
);
|
||||
let tmp = Arc::new(Mutex::new(ctrl));
|
||||
DbusKbdBacklight::new(tmp.clone()).add_to_server(&mut object_server);
|
||||
tasks.push(tmp);
|
||||
}
|
||||
|
||||
// TODO: implement messaging between threads to check fails
|
||||
// These tasks generally read a sys path or file to check for a
|
||||
// change
|
||||
let _handle = std::thread::Builder::new()
|
||||
.name("asusd watch".to_string())
|
||||
.spawn(move || loop {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
||||
for ctrl in tasks.iter() {
|
||||
if let Ok(mut lock) = ctrl.try_lock() {
|
||||
lock.do_task().ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
if let Err(err) = object_server.try_handle_next() {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
use crate::config::Config;
|
||||
use asus_nb::{aura_modes::AuraModes, DBUS_IFACE, DBUS_PATH};
|
||||
use dbus::tree::{Factory, MTSync, Method, MethodErr, Signal, Tree};
|
||||
use log::warn;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
fn set_keyboard_backlight(sender: Mutex<Sender<AuraModes>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetKeyBacklight", (), {
|
||||
move |m| {
|
||||
let json: &str = m.msg.read1()?;
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
if let Ok(data) = serde_json::from_str(json) {
|
||||
lock.try_send(data).unwrap_or_else(|err| {
|
||||
warn!("SetKeyBacklight over mpsc failed: {}", err)
|
||||
});
|
||||
} else {
|
||||
warn!("SetKeyBacklight could not deserialise");
|
||||
}
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<&str, _>("json")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn get_keyboard_backlight(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetKeyBacklight", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
for mode in &lock.kbd_backlight_modes {
|
||||
if lock.kbd_backlight_mode == <u8>::from(mode) {
|
||||
let mode = serde_json::to_string(&mode).unwrap();
|
||||
let mret = m.msg.method_return().append1(mode);
|
||||
return Ok(vec![mret]);
|
||||
}
|
||||
}
|
||||
Err(MethodErr::failed(
|
||||
"Keyboard LED mode set to an invalid mode",
|
||||
))
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<&str, _>("json")
|
||||
}
|
||||
|
||||
fn get_keyboard_backlight_modes(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetKeyBacklightModes", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mode = serde_json::to_string(&lock.kbd_backlight_modes).unwrap();
|
||||
let mret = m.msg.method_return().append1(mode);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<&str, _>("json")
|
||||
}
|
||||
|
||||
fn set_animatrix(
|
||||
sender: Mutex<Sender<Vec<Vec<u8>>>>, // need mutex only to get interior mutability in MTSync
|
||||
) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("AnimatrixWrite", (), {
|
||||
move |m| {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte_array: Vec<Vec<u8>> = vec![iter.read()?, iter.read()?];
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
// Ignore errors if the channel is already full
|
||||
lock.try_send(byte_array).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<Vec<u8>, _>("bytearray1")
|
||||
.inarg::<Vec<u8>, _>("bytearray2")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn set_fan_mode(sender: Mutex<Sender<u8>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetFanMode", (), {
|
||||
move |m| {
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte: u8 = iter.read()?;
|
||||
lock.try_send(byte).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<u8, _>("mode")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
fn get_fan_mode(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetFanMode", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mret = m.msg.method_return().append1(lock.power_profile);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<u8, _>("mode")
|
||||
}
|
||||
|
||||
fn get_charge_limit(config: Arc<Mutex<Config>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
.method("GetChargeLimit", (), {
|
||||
move |m| {
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
let mret = m.msg.method_return().append1(lock.bat_charge_limit);
|
||||
Ok(vec![mret])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock config for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.outarg::<u8, _>("limit")
|
||||
}
|
||||
|
||||
fn set_charge_limit(sender: Mutex<Sender<u8>>) -> Method<MTSync, ()> {
|
||||
let factory = Factory::new_sync::<()>();
|
||||
factory
|
||||
// method for ledmessage
|
||||
.method("SetChargeLimit", (), {
|
||||
move |m| {
|
||||
if let Ok(mut lock) = sender.try_lock() {
|
||||
let mut iter = m.msg.iter_init();
|
||||
let byte: u8 = iter.read()?;
|
||||
lock.try_send(byte).unwrap_or_else(|_err| {});
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Err(MethodErr::failed("Could not lock daemon for access"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.inarg::<u8, _>("limit")
|
||||
.annotate("org.freedesktop.DBus.Method.NoReply", "true")
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn dbus_create_tree(
|
||||
config: Arc<Mutex<Config>>,
|
||||
) -> (
|
||||
Tree<MTSync, ()>,
|
||||
Receiver<AuraModes>,
|
||||
Receiver<Vec<Vec<u8>>>,
|
||||
Receiver<u8>,
|
||||
Receiver<u8>,
|
||||
Arc<Signal<()>>,
|
||||
Arc<Signal<()>>,
|
||||
Arc<Signal<()>>,
|
||||
) {
|
||||
let (aura_command_send, aura_command_recv) = channel::<AuraModes>(1);
|
||||
let (animatrix_send, animatrix_recv) = channel::<Vec<Vec<u8>>>(1);
|
||||
let (fan_mode_send, fan_mode_recv) = channel::<u8>(1);
|
||||
let (charge_send, charge_recv) = channel::<u8>(1);
|
||||
|
||||
let factory = Factory::new_sync::<()>();
|
||||
|
||||
let key_backlight_changed = Arc::new(
|
||||
factory
|
||||
.signal("KeyBacklightChanged", ())
|
||||
.sarg::<&str, _>("json"),
|
||||
);
|
||||
let chrg_limit_changed = Arc::new(
|
||||
factory
|
||||
.signal("ChargeLimitChanged", ())
|
||||
.sarg::<u8, _>("limit"),
|
||||
);
|
||||
let fanmode_changed = Arc::new(factory.signal("FanModeChanged", ()).sarg::<u8, _>("mode"));
|
||||
|
||||
let tree = factory
|
||||
.tree(())
|
||||
.add(
|
||||
factory.object_path(DBUS_PATH, ()).introspectable().add(
|
||||
factory
|
||||
.interface(DBUS_IFACE, ())
|
||||
.add_m(set_keyboard_backlight(Mutex::new(aura_command_send)))
|
||||
.add_m(set_animatrix(Mutex::new(animatrix_send)))
|
||||
.add_m(set_fan_mode(Mutex::new(fan_mode_send)))
|
||||
.add_m(set_charge_limit(Mutex::new(charge_send)))
|
||||
.add_m(get_fan_mode(config.clone()))
|
||||
.add_m(get_charge_limit(config.clone()))
|
||||
.add_m(get_keyboard_backlight(config.clone()))
|
||||
.add_m(get_keyboard_backlight_modes(config))
|
||||
.add_s(key_backlight_changed.clone())
|
||||
.add_s(fanmode_changed.clone())
|
||||
.add_s(chrg_limit_changed.clone()),
|
||||
),
|
||||
)
|
||||
.add(factory.object_path("/", ()).introspectable());
|
||||
(
|
||||
tree,
|
||||
aura_command_recv,
|
||||
animatrix_recv,
|
||||
fan_mode_recv,
|
||||
charge_recv,
|
||||
key_backlight_changed,
|
||||
fanmode_changed,
|
||||
chrg_limit_changed,
|
||||
)
|
||||
}
|
||||
@@ -1,19 +1,58 @@
|
||||
use std::fmt;
|
||||
use std::convert::From;
|
||||
use intel_pstate::PStateError;
|
||||
use rog_fan_curve::CurveError;
|
||||
|
||||
#[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),
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RogError {}
|
||||
|
||||
impl From<PStateError> for RogError {
|
||||
fn from(err: PStateError) -> Self {
|
||||
RogError::IntelPstate(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CurveError> for RogError {
|
||||
fn from(err: CurveError) -> Self {
|
||||
RogError::FanCurve(err)
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,11 @@ use std::io::Read;
|
||||
|
||||
pub static LEDMODE_CONFIG_PATH: &str = "/etc/asusd/asusd-ledmodes.toml";
|
||||
|
||||
static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl";
|
||||
pub static HELP_ADDRESS: &str = "https://gitlab.com/asus-linux/asus-nb-ctrl";
|
||||
|
||||
pub struct LaptopBase {
|
||||
usb_product: String,
|
||||
condev_iface: Option<String>, // required for finding the Consumer Device interface
|
||||
supported_modes: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -17,6 +18,9 @@ impl LaptopBase {
|
||||
pub fn usb_product(&self) -> &str {
|
||||
&self.usb_product
|
||||
}
|
||||
pub fn condev_iface(&self) -> Option<&String> {
|
||||
self.condev_iface.as_ref()
|
||||
}
|
||||
pub fn supported_modes(&self) -> &[u8] {
|
||||
&self.supported_modes
|
||||
}
|
||||
@@ -37,6 +41,7 @@ pub fn match_laptop() -> Option<LaptopBase> {
|
||||
info!("Found GL753 or similar");
|
||||
return Some(LaptopBase {
|
||||
usb_product: "1854".to_string(),
|
||||
condev_iface: None,
|
||||
supported_modes: vec![STATIC, BREATHING, STROBE],
|
||||
});
|
||||
}
|
||||
@@ -44,6 +49,11 @@ pub fn match_laptop() -> Option<LaptopBase> {
|
||||
}
|
||||
}
|
||||
}
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
None
|
||||
}
|
||||
|
||||
@@ -58,6 +68,7 @@ fn select_1866_device(prod: String) -> LaptopBase {
|
||||
|
||||
let mut laptop = LaptopBase {
|
||||
usb_product: prod,
|
||||
condev_iface: Some("02".to_owned()),
|
||||
supported_modes: vec![],
|
||||
};
|
||||
|
||||
@@ -67,13 +78,6 @@ fn select_1866_device(prod: String) -> LaptopBase {
|
||||
return laptop;
|
||||
}
|
||||
}
|
||||
|
||||
warn!(
|
||||
"Unsupported laptop, please request support at {}",
|
||||
HELP_ADDRESS
|
||||
);
|
||||
warn!("Continuing with minimal support");
|
||||
|
||||
laptop
|
||||
}
|
||||
|
||||
|
||||
@@ -10,36 +10,31 @@ pub mod ctrl_fan_cpu;
|
||||
///
|
||||
pub mod ctrl_leds;
|
||||
///
|
||||
pub mod dbus;
|
||||
/// Laptop matching to determine capabilities
|
||||
pub mod laptops;
|
||||
|
||||
mod error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use config::Config;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc::Receiver, Mutex};
|
||||
use tokio::task::JoinHandle;
|
||||
use crate::error::RogError;
|
||||
use zbus::ObjectServer;
|
||||
|
||||
pub static VERSION: &str = "1.0.3";
|
||||
pub static VERSION: &str = "2.0.3";
|
||||
|
||||
use ::dbus::{nonblock::SyncConnection, tree::Signal};
|
||||
pub trait Reloadable {
|
||||
fn reload(&mut self) -> Result<(), RogError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Controller {
|
||||
pub trait ZbusAdd {
|
||||
fn add_to_server(self, server: &mut ObjectServer);
|
||||
}
|
||||
|
||||
pub trait CtrlTask {
|
||||
fn do_task(&mut self) -> Result<(), RogError>;
|
||||
}
|
||||
|
||||
pub trait CtrlTaskComplex {
|
||||
type A;
|
||||
|
||||
async fn reload_from_config(&mut self, config: &mut Config) -> Result<(), Box<dyn Error>>;
|
||||
|
||||
/// Spawn an infinitely running task (usually) which checks a Receiver for input,
|
||||
/// and may send a signal over dbus
|
||||
fn spawn_task_loop(
|
||||
self,
|
||||
config: Arc<Mutex<Config>>,
|
||||
recv: Receiver<Self::A>,
|
||||
connection: Option<Arc<SyncConnection>>,
|
||||
signal: Option<Arc<Signal<()>>>,
|
||||
) -> Vec<JoinHandle<()>>;
|
||||
fn do_task(&mut self, config: &mut Config, event: Self::A);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use asus_nb::{
|
||||
cli_options::{LedBrightness, SetAuraBuiltin},
|
||||
core_dbus::AuraDbusClient,
|
||||
profile::{ProfileCommand, ProfileEvent},
|
||||
};
|
||||
use ctrl_gfx::vendors::GfxVendors;
|
||||
use daemon::ctrl_fan_cpu::FanLevel;
|
||||
use gumdrop::Options;
|
||||
use log::LevelFilter;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use yansi_term::Colour::Green;
|
||||
use yansi_term::Colour::Red;
|
||||
|
||||
#[derive(Options)]
|
||||
struct CLIStart {
|
||||
@@ -20,13 +25,17 @@ struct CLIStart {
|
||||
#[options(meta = "CHRG", help = "<20-100>")]
|
||||
chg_limit: Option<u8>,
|
||||
#[options(command)]
|
||||
command: Option<Command>,
|
||||
command: Option<CliCommand>,
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
enum Command {
|
||||
enum CliCommand {
|
||||
#[options(help = "Set the keyboard lighting from built-in modes")]
|
||||
LedMode(LedModeCommand),
|
||||
#[options(help = "Create and configure profiles")]
|
||||
Profile(ProfileCommand),
|
||||
#[options(help = "Set the graphics mode")]
|
||||
Graphics(GraphicsCommand),
|
||||
}
|
||||
|
||||
#[derive(Options)]
|
||||
@@ -37,8 +46,21 @@ struct LedModeCommand {
|
||||
command: Option<SetAuraBuiltin>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Options)]
|
||||
struct GraphicsCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "Set graphics mode: <nvidia, hybrid, compute, integrated>")]
|
||||
mode: Option<GfxVendors>,
|
||||
#[options(help = "Get the current mode")]
|
||||
get: bool,
|
||||
#[options(help = "Get the current power status")]
|
||||
pow: bool,
|
||||
#[options(help = "Do not ask for confirmation")]
|
||||
force: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut logger = env_logger::Builder::new();
|
||||
logger
|
||||
.target(env_logger::Target::Stdout)
|
||||
@@ -54,11 +76,19 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let writer = AuraDbusClient::new()?;
|
||||
|
||||
if let Some(Command::LedMode(mode)) = parsed.command {
|
||||
if let Some(command) = mode.command {
|
||||
writer.write_builtin_mode(&command.into())?
|
||||
match parsed.command {
|
||||
Some(CliCommand::LedMode(mode)) => {
|
||||
if let Some(command) = mode.command {
|
||||
writer.write_builtin_mode(&command.into())?
|
||||
}
|
||||
}
|
||||
Some(CliCommand::Profile(command)) => {
|
||||
writer.write_profile_command(&ProfileEvent::Cli(command))?
|
||||
}
|
||||
Some(CliCommand::Graphics(command)) => do_gfx(command, &writer)?,
|
||||
None => (),
|
||||
}
|
||||
|
||||
if let Some(brightness) = parsed.kbd_bright {
|
||||
writer.write_brightness(brightness.level())?;
|
||||
}
|
||||
@@ -70,3 +100,92 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_gfx(
|
||||
command: GraphicsCommand,
|
||||
writer: &AuraDbusClient,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if let Some(mode) = command.mode {
|
||||
println!("Updating settings, please wait...");
|
||||
println!("If this takes longer than 30s, ctrl+c then check journalctl");
|
||||
|
||||
writer.write_gfx_mode(mode)?;
|
||||
let res = writer.wait_gfx_changed()?;
|
||||
match res.as_str() {
|
||||
"reboot" => {
|
||||
println!(
|
||||
"{}",
|
||||
Green.paint("\nGraphics vendor mode changed successfully\n"),
|
||||
);
|
||||
do_gfx_action(
|
||||
command.force,
|
||||
Command::new("systemctl").arg("reboot -i"),
|
||||
"Reboot Linux PC",
|
||||
"Please reboot when ready",
|
||||
)?;
|
||||
}
|
||||
"restartx" => {
|
||||
println!(
|
||||
"{}",
|
||||
Green.paint("\nGraphics vendor mode changed successfully\n")
|
||||
);
|
||||
do_gfx_action(
|
||||
command.force,
|
||||
Command::new("systemctl")
|
||||
.arg("restart")
|
||||
.arg("display-manager.service"),
|
||||
"Restart display-manager server",
|
||||
"Please restart display-manager when ready",
|
||||
)?;
|
||||
std::process::exit(1)
|
||||
}
|
||||
_ => std::process::exit(-1),
|
||||
}
|
||||
std::process::exit(-1)
|
||||
}
|
||||
if command.get {
|
||||
let res = writer.get_gfx_mode()?;
|
||||
println!("Current graphics mode: {}", res);
|
||||
}
|
||||
if command.pow {
|
||||
let res = writer.get_gfx_pwr()?;
|
||||
if res.contains("active") {
|
||||
println!("Current power status: {}", Red.paint(&format!("{}", res)));
|
||||
} else {
|
||||
println!("Current power status: {}", Green.paint(&format!("{}", res)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_gfx_action(
|
||||
no_confirm: bool,
|
||||
command: &mut Command,
|
||||
ask_msg: &str,
|
||||
cancel_msg: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("{}? y/n", ask_msg);
|
||||
|
||||
let mut buf = String::new();
|
||||
if no_confirm {
|
||||
let status = command.status()?;
|
||||
|
||||
if !status.success() {
|
||||
println!("systemctl: returned with {}", status);
|
||||
}
|
||||
}
|
||||
|
||||
std::io::stdin().read_line(&mut buf).expect("Input failed");
|
||||
let input = buf.chars().next().unwrap() as char;
|
||||
|
||||
if input == 'Y' || input == 'y' || no_confirm {
|
||||
let status = command.status()?;
|
||||
|
||||
if !status.success() {
|
||||
println!("systemctl: returned with {}", status);
|
||||
}
|
||||
} else {
|
||||
println!("{}", Red.paint(&format!("{}", cancel_msg)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "asus-nb"
|
||||
version = "0.15.0"
|
||||
version = "2.0.2"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
@@ -16,6 +16,10 @@ serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
yansi-term = "^0.1"
|
||||
rog_fan_curve = { version = "0.1", features = ["serde"] }
|
||||
zbus = "1.1.1"
|
||||
zvariant = "2.2.0"
|
||||
ctrl-gfx = { path = "../ctrl-gfx" }
|
||||
|
||||
[dev-dependencies]
|
||||
tinybmp = "^0.2.3"
|
||||
@@ -93,6 +93,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
writer.write_colour_block(&colours)?;
|
||||
|
||||
// can change 100 times per second, so need to slow it down
|
||||
//std::thread::sleep(std::time::Duration::from_millis(30));
|
||||
std::thread::sleep(std::time::Duration::from_millis(30));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::anime_matrix::AniMePacketType;
|
||||
use crate::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||
use crate::{DBUS_IFACE, DBUS_NAME};
|
||||
use dbus::channel::Sender;
|
||||
use dbus::{blocking::Connection, Message};
|
||||
use std::error::Error;
|
||||
@@ -65,8 +65,9 @@ impl AniMeDbusWriter {
|
||||
image[1][..7].copy_from_slice(&ANIME_PANE2_PREFIX);
|
||||
}
|
||||
|
||||
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "AnimatrixWrite")?
|
||||
.append2(image[0].to_vec(), image[1].to_vec());
|
||||
let mut msg =
|
||||
Message::new_method_call(DBUS_NAME, "/org/asuslinux/Anime", DBUS_IFACE, "SetAnime")?
|
||||
.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));
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -1,55 +1,193 @@
|
||||
use super::*;
|
||||
use crate::fancy::KeyColourArray;
|
||||
use dbus::channel::Sender;
|
||||
use dbus::{blocking::Connection, channel::Token, Message};
|
||||
use crate::profile::ProfileEvent;
|
||||
use ctrl_gfx::vendors::GfxVendors;
|
||||
use dbus::{blocking::Connection, Message};
|
||||
use std::error::Error;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
Arc, Mutex,
|
||||
};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use crate::dbus_charge::{OrgAsuslinuxDaemonNotifyCharge, OrgAsuslinuxDaemon as OrgAsuslinuxDaemonCharge};
|
||||
use crate::dbus_gfx::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonGfx, OrgAsuslinuxDaemonNotifyAction,
|
||||
OrgAsuslinuxDaemonNotifyGfx,
|
||||
};
|
||||
use crate::dbus_ledmode::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonLed, OrgAsuslinuxDaemonNotifyLed,
|
||||
};
|
||||
use crate::dbus_profile::{
|
||||
OrgAsuslinuxDaemon as OrgAsuslinuxDaemonProfile, OrgAsuslinuxDaemonNotifyProfile,
|
||||
};
|
||||
|
||||
// Signals separated out
|
||||
pub struct CtrlSignals {
|
||||
pub gfx_vendor_signal: Arc<Mutex<Option<String>>>,
|
||||
pub gfx_action_signal: Arc<Mutex<Option<String>>>,
|
||||
pub profile_signal: Arc<Mutex<Option<String>>>,
|
||||
pub ledmode_signal: Arc<Mutex<Option<AuraModes>>>,
|
||||
pub charge_signal: Arc<Mutex<Option<u8>>>,
|
||||
}
|
||||
|
||||
impl CtrlSignals {
|
||||
#[inline]
|
||||
pub fn new(connection: &Connection) -> Result<Self, Box<dyn Error>> {
|
||||
let proxy = connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
|
||||
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_millis(5000),
|
||||
);
|
||||
|
||||
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_millis(5000),
|
||||
);
|
||||
|
||||
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_millis(5000),
|
||||
);
|
||||
|
||||
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_micros(500))?;
|
||||
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_millis(5000),
|
||||
);
|
||||
proxy.set_led_mode(&serde_json::to_string(&mode)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -68,15 +206,13 @@ impl AuraDbusClient {
|
||||
vecs.push(v.to_vec());
|
||||
}
|
||||
let mode = AuraModes::PerKey(vecs);
|
||||
let mut msg =
|
||||
Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetKeyBacklight")?
|
||||
.append1(serde_json::to_string(&mode)?);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
|
||||
self.write_keyboard_leds(&mode)?;
|
||||
|
||||
thread::sleep(Duration::from_micros(self.block_time));
|
||||
self.connection.process(Duration::from_micros(500))?;
|
||||
|
||||
if self.stop.load(Ordering::Relaxed) {
|
||||
self.connection.remove_match(self.stop_token)?;
|
||||
println!("Keyboard backlight was changed, exiting");
|
||||
std::process::exit(1)
|
||||
}
|
||||
@@ -85,29 +221,81 @@ 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_millis(5000),
|
||||
);
|
||||
proxy.set_led_mode(&serde_json::to_string(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_millis(5000),
|
||||
);
|
||||
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_millis(5000),
|
||||
);
|
||||
let x = proxy.vendor()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_gfx_mode(&self, vendor: GfxVendors) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Gfx",
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
proxy.set_vendor(<&str>::from(&vendor))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_fan_mode(&self, level: u8) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut msg = Message::new_method_call(DBUS_NAME, DBUS_PATH, DBUS_IFACE, "SetFanMode")?
|
||||
.append1(level);
|
||||
msg.set_no_reply(true);
|
||||
self.connection.send(msg).unwrap();
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
proxy.set_profile(&serde_json::to_string(&ProfileEvent::ChangeMode(level))?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_profile_command(
|
||||
&self,
|
||||
cmd: &ProfileEvent,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let proxy = self.connection.with_proxy(
|
||||
"org.asuslinux.Daemon",
|
||||
"/org/asuslinux/Profile",
|
||||
Duration::from_millis(5000),
|
||||
);
|
||||
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_millis(5000),
|
||||
);
|
||||
proxy.set_limit(level)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
46
asus-nb/src/dbus_charge.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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 as 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";
|
||||
}
|
||||
76
asus-nb/src/dbus_gfx.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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 as 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";
|
||||
}
|
||||
52
asus-nb/src/dbus_ledmode.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 as dbus;
|
||||
#[allow(unused_imports)]
|
||||
use dbus::arg;
|
||||
use dbus::blocking;
|
||||
|
||||
pub trait OrgAsuslinuxDaemon {
|
||||
fn set_led_mode(&self, data: &str) -> Result<(), dbus::Error>;
|
||||
fn led_mode(&self) -> Result<String, dbus::Error>;
|
||||
fn led_modes(&self) -> Result<String, 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 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, ))
|
||||
}
|
||||
}
|
||||
|
||||
#[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";
|
||||
}
|
||||
51
asus-nb/src/dbus_profile.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
// This code was autogenerated with `dbus-codegen-rust -s -d org.asuslinux.Daemon -f org.asuslinux.Daemon -c blocking -p /org/asuslinux/Profile -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_profile(&self, profile: &str) -> Result<(), 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 profile(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Profile", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
|
||||
fn profiles(&self) -> Result<String, dbus::Error> {
|
||||
self.method_call("org.asuslinux.Daemon", "Profiles", ())
|
||||
.and_then(|r: (String,)| Ok(r.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OrgAsuslinuxDaemonNotifyProfile {
|
||||
pub profile: String,
|
||||
}
|
||||
|
||||
impl arg::AppendAll for OrgAsuslinuxDaemonNotifyProfile {
|
||||
fn append(&self, i: &mut arg::IterAppend) {
|
||||
arg::RefArg::append(&self.profile, i);
|
||||
}
|
||||
}
|
||||
|
||||
impl arg::ReadAll for OrgAsuslinuxDaemonNotifyProfile {
|
||||
fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
|
||||
Ok(OrgAsuslinuxDaemonNotifyProfile { profile: i.read()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl dbus::message::SignalArgs for OrgAsuslinuxDaemonNotifyProfile {
|
||||
const NAME: &'static str = "NotifyProfile";
|
||||
const INTERFACE: &'static str = "org.asuslinux.Daemon";
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuraError {
|
||||
ParseColour,
|
||||
ParseSpeed,
|
||||
@@ -18,3 +20,21 @@ impl fmt::Display for AuraError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for AuraError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GraphicsError {
|
||||
ParseVendor,
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphicsError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GraphicsError::ParseVendor => write!(f, "Could not parse vendor name"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GraphicsError {}
|
||||
|
||||
@@ -6,6 +6,8 @@ pub const LED_MSG_LEN: usize = 17;
|
||||
pub mod aura_modes;
|
||||
use aura_modes::AuraModes;
|
||||
|
||||
pub mod profile;
|
||||
|
||||
/// Contains mostly only what is required for parsing CLI options
|
||||
pub mod cli_options;
|
||||
|
||||
@@ -21,6 +23,11 @@ pub mod anime_dbus;
|
||||
/// Helper functions for the AniMe display
|
||||
pub mod anime_matrix;
|
||||
|
||||
pub mod dbus_gfx;
|
||||
pub mod dbus_ledmode;
|
||||
pub mod dbus_profile;
|
||||
pub mod dbus_charge;
|
||||
|
||||
pub mod error;
|
||||
|
||||
// static LED_INIT1: [u8; 2] = [0x5d, 0xb9];
|
||||
|
||||
95
asus-nb/src/profile.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use gumdrop::Options;
|
||||
use rog_fan_curve::{Curve, Fan};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ProfileEvent {
|
||||
Cli(ProfileCommand),
|
||||
ChangeMode(u8),
|
||||
Toggle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FanLevel {
|
||||
Normal,
|
||||
Boost,
|
||||
Silent,
|
||||
}
|
||||
|
||||
impl FromStr for FanLevel {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"normal" => Ok(FanLevel::Normal),
|
||||
"boost" => Ok(FanLevel::Boost),
|
||||
"silent" => Ok(FanLevel::Silent),
|
||||
_ => Err("Invalid fan level"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for FanLevel {
|
||||
fn from(n: u8) -> Self {
|
||||
match n {
|
||||
0 => FanLevel::Normal,
|
||||
1 => FanLevel::Boost,
|
||||
2 => FanLevel::Silent,
|
||||
_ => FanLevel::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FanLevel> for u8 {
|
||||
fn from(n: FanLevel) -> Self {
|
||||
match n {
|
||||
FanLevel::Normal => 0,
|
||||
FanLevel::Boost => 1,
|
||||
FanLevel::Silent => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FanLevel> for u8 {
|
||||
fn from(n: &FanLevel) -> Self {
|
||||
match n {
|
||||
FanLevel::Normal => 0,
|
||||
FanLevel::Boost => 1,
|
||||
FanLevel::Silent => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fan_curve(data: &str) -> Result<Curve, String> {
|
||||
let curve = Curve::from_config_str(data)?;
|
||||
if let Err(err) = curve.check_safety(Fan::Cpu) {
|
||||
return Err(format!("Unsafe curve {:?}", err));
|
||||
}
|
||||
if let Err(err) = curve.check_safety(Fan::Gpu) {
|
||||
return Err(format!("Unsafe curve {:?}", err));
|
||||
}
|
||||
Ok(curve)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Options, Serialize, Deserialize)]
|
||||
pub struct ProfileCommand {
|
||||
#[options(help = "print help message")]
|
||||
help: bool,
|
||||
#[options(help = "create the profile if it doesn't exist")]
|
||||
pub create: bool,
|
||||
|
||||
#[options(help = "enable or disable cpu turbo")]
|
||||
pub turbo: Option<bool>,
|
||||
#[options(help = "set min cpu scaling (intel)")]
|
||||
pub min_percentage: Option<u8>,
|
||||
#[options(help = "set max cpu scaling (intel)")]
|
||||
pub max_percentage: Option<u8>,
|
||||
|
||||
#[options(meta = "PWR", help = "<silent, normal, boost>")]
|
||||
pub preset: Option<FanLevel>,
|
||||
#[options(parse(try_from_str = "parse_fan_curve"), help = "set fan curve")]
|
||||
pub curve: Option<Curve>,
|
||||
#[options(free)]
|
||||
pub profile: Option<String>,
|
||||
}
|
||||
17
asus-notify/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "asus-notify"
|
||||
version = "1.0.1"
|
||||
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"
|
||||
notify-rust = "^4.0.0"
|
||||
dbus = { version = "^0.8" }
|
||||
asus-nb = { path = "../asus-nb" }
|
||||
asus-nb-ctrl = { path = "../asus-nb-ctrl" }
|
||||
103
asus-notify/src/main.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
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 1.0.1");
|
||||
|
||||
let mut cfg = Config::read_new()?;
|
||||
let mut last_profile = String::new();
|
||||
|
||||
let connection = Connection::new_system()?;
|
||||
let signals = CtrlSignals::new(&connection)?;
|
||||
|
||||
let mut last_profile_notif: Option<NotificationHandle> = None;
|
||||
let mut last_led_notif: Option<NotificationHandle> = None;
|
||||
let mut last_gfx_notif: Option<NotificationHandle> = None;
|
||||
let mut last_chrg_notif: Option<NotificationHandle> = None;
|
||||
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
connection.process(std::time::Duration::from_millis(200))?;
|
||||
|
||||
if let Ok(mut lock) = signals.gfx_vendor_signal.lock() {
|
||||
if let Some(vendor) = lock.take() {
|
||||
if let Some(notif) = last_gfx_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!("Graphics mode changed to {}", vendor))?;
|
||||
last_gfx_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.charge_signal.lock() {
|
||||
if let Some(limit) = lock.take() {
|
||||
if let Some(notif) = last_chrg_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!("Battery charge limit changed to {}", limit))?;
|
||||
last_led_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut lock) = signals.ledmode_signal.lock() {
|
||||
if let Some(ledmode) = lock.take() {
|
||||
if let Some(notif) = last_led_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
let x = do_notif(&format!(
|
||||
"Keyboard LED mode changed to {}",
|
||||
<&str>::from(&ledmode)
|
||||
))?;
|
||||
last_led_notif = Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to do the config read because of a limitation preventing
|
||||
// easy dbus notification from the profile controller
|
||||
cfg.read();
|
||||
if last_profile != cfg.active_profile {
|
||||
if let Some(notif) = last_profile_notif.take() {
|
||||
notif.close();
|
||||
}
|
||||
if let Some(profile) = cfg.power_profiles.get(&cfg.active_profile) {
|
||||
let x = do_thermal_notif(&profile, &cfg.active_profile)?;
|
||||
last_profile_notif = Some(x);
|
||||
last_profile = cfg.active_profile.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_thermal_notif(profile: &Profile, label: &str) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||
let fan = profile.fan_preset;
|
||||
let turbo = if profile.turbo { "enabled" } else { "disabled" };
|
||||
let icon = match fan {
|
||||
0 => "asus_notif_yellow",
|
||||
1 => "asus_notif_red",
|
||||
2 => "asus_notif_green",
|
||||
_ => "asus_notif_red",
|
||||
};
|
||||
let x = Notification::new()
|
||||
.summary("ASUS ROG")
|
||||
.body(&format!("Thermal profile changed to {}, turbo {}", label.to_uppercase(), turbo))
|
||||
.hint(Hint::Resident(true))
|
||||
.timeout(2000)
|
||||
.hint(Hint::Category("device".into()))
|
||||
//.hint(Hint::Transient(true))
|
||||
.icon(icon)
|
||||
.show()?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn do_notif(body: &str) -> Result<NotificationHandle, Box<dyn Error>> {
|
||||
let x = Notification::new()
|
||||
.summary("ASUS ROG")
|
||||
.body(body)
|
||||
.timeout(2000)
|
||||
.show()?;
|
||||
Ok(x)
|
||||
}
|
||||
19
ctrl-gfx/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "ctrl-gfx"
|
||||
version = "2.1.1"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
description = "Fine control of laptop GPU"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
sysfs-class = "^0.1.2"
|
||||
log = "^0.4"
|
||||
|
||||
zbus = { version = "1.1.1", optional = true }
|
||||
zvariant = { version = "2.2.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["use-zbus"]
|
||||
use-zbus = ["zbus", "zvariant"]
|
||||
363
ctrl-gfx/src/ctrl_gfx.rs
Normal file
@@ -0,0 +1,363 @@
|
||||
use log::{error, info, warn};
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
use zbus::dbus_interface;
|
||||
|
||||
use crate::vendors::*;
|
||||
use crate::*;
|
||||
use crate::{error::GfxError, system::*};
|
||||
|
||||
pub struct CtrlGraphics {
|
||||
bus: PciBus,
|
||||
_amd: Vec<GraphicsDevice>,
|
||||
_intel: Vec<GraphicsDevice>,
|
||||
nvidia: Vec<GraphicsDevice>,
|
||||
#[allow(dead_code)]
|
||||
other: Vec<GraphicsDevice>,
|
||||
initfs_cmd: Option<Command>,
|
||||
}
|
||||
|
||||
trait Dbus {
|
||||
fn vendor(&self) -> String;
|
||||
fn power(&self) -> String;
|
||||
fn set_vendor(&mut self, vendor: String);
|
||||
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()>;
|
||||
fn notify_action(&self, action: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||
impl Dbus for CtrlGraphics {
|
||||
fn vendor(&self) -> String {
|
||||
Self::get_vendor().unwrap_or_else(|err| format!("Get vendor failed: {}", err))
|
||||
}
|
||||
|
||||
fn power(&self) -> String {
|
||||
Self::get_runtime_status().unwrap_or_else(|err| format!("Get power status failed: {}", err))
|
||||
}
|
||||
|
||||
fn set_vendor(&mut self, vendor: String) {
|
||||
if let Ok(tmp) = GfxVendors::from_str(&vendor) {
|
||||
let action = self.set(tmp).unwrap_or_else(|err| {
|
||||
warn!("{}", err);
|
||||
format!("Failed: {}", err.to_string())
|
||||
});
|
||||
self.notify_gfx(&vendor)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
self.notify_action(&action)
|
||||
.unwrap_or_else(|err| warn!("{}", err));
|
||||
}
|
||||
}
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_gfx(&self, vendor: &str) -> zbus::Result<()>;
|
||||
|
||||
#[dbus_interface(signal)]
|
||||
fn notify_action(&self, action: &str) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
impl CtrlGraphics {
|
||||
pub fn new() -> std::io::Result<CtrlGraphics> {
|
||||
let bus = PciBus::new()?;
|
||||
|
||||
info!("Rescanning PCI bus");
|
||||
bus.rescan()?;
|
||||
|
||||
let devs = PciDevice::all()?;
|
||||
|
||||
let functions = |parent: &PciDevice| -> Vec<PciDevice> {
|
||||
let mut functions = Vec::new();
|
||||
if let Some(parent_slot) = parent.id().split('.').next() {
|
||||
for func in devs.iter() {
|
||||
if let Some(func_slot) = func.id().split('.').next() {
|
||||
if func_slot == parent_slot {
|
||||
info!("{}: Function for {}", func.id(), parent.id());
|
||||
functions.push(func.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
functions
|
||||
};
|
||||
|
||||
let mut amd = Vec::new();
|
||||
let mut intel = Vec::new();
|
||||
let mut nvidia = Vec::new();
|
||||
let mut other = Vec::new();
|
||||
for dev in devs.iter() {
|
||||
let c = dev.class()?;
|
||||
if 0x03 == (c >> 16) & 0xFF {
|
||||
match dev.vendor()? {
|
||||
0x1002 => {
|
||||
info!("{}: AMD graphics", dev.id());
|
||||
amd.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
0x10DE => {
|
||||
info!("{}: NVIDIA graphics", dev.id());
|
||||
nvidia.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
0x8086 => {
|
||||
info!("{}: Intel graphics", dev.id());
|
||||
intel.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
vendor => {
|
||||
info!("{}: Other({:X}) graphics", dev.id(), vendor);
|
||||
other.push(GraphicsDevice::new(dev.id().to_owned(), functions(&dev)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut initfs_cmd = None;
|
||||
|
||||
if Path::new(INITRAMFS_PATH).exists() {
|
||||
let mut cmd = Command::new("update-initramfs");
|
||||
cmd.arg("-u");
|
||||
initfs_cmd = Some(cmd);
|
||||
info!("Using initramfs update command 'update-initramfs'");
|
||||
}
|
||||
// } else if Path::new(DRACUT_PATH).exists() {
|
||||
// let mut cmd = Command::new("dracut");
|
||||
// cmd.arg("-f");
|
||||
// initfs_cmd = Some(cmd);
|
||||
// info!("Using initramfs update command 'dracut'");
|
||||
// }
|
||||
|
||||
Ok(CtrlGraphics {
|
||||
bus,
|
||||
_amd: amd,
|
||||
_intel: intel,
|
||||
nvidia,
|
||||
other,
|
||||
initfs_cmd,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-zbus")]
|
||||
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||
server
|
||||
.at(&"/org/asuslinux/Gfx".try_into().unwrap(), self)
|
||||
.map_err(|err| {
|
||||
warn!("CtrlGraphics: add_to_server {}", err);
|
||||
err
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn reload(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
self.auto_power()?;
|
||||
info!("Reloaded gfx mode: {:?}", CtrlGraphics::get_vendor()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn can_switch(&self) -> bool {
|
||||
// !self.nvidia.is_empty() && (!self.intel.is_empty() || !self.amd.is_empty())
|
||||
// }
|
||||
|
||||
fn get_prime_discrete() -> Result<String, GfxError> {
|
||||
let s = std::fs::read_to_string(PRIME_DISCRETE_PATH)
|
||||
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn set_prime_discrete(mode: &str) -> Result<(), GfxError> {
|
||||
std::fs::write(PRIME_DISCRETE_PATH, mode)
|
||||
.map_err(|err| GfxError::Read(PRIME_DISCRETE_PATH.into(), err))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Associated method to get which vendor mode is set
|
||||
pub fn get_vendor() -> Result<String, GfxError> {
|
||||
let modules = Module::all().map_err(|err| GfxError::Read("get_vendor".into(), err))?;
|
||||
let vendor = if modules
|
||||
.iter()
|
||||
.any(|module| module.name == "nouveau" || module.name == "nvidia")
|
||||
{
|
||||
info!("nvidia or nouveau module found");
|
||||
let mode = match Self::get_prime_discrete() {
|
||||
Ok(m) => m,
|
||||
Err(_) => "nvidia".to_string(),
|
||||
};
|
||||
|
||||
if mode == "on-demand" {
|
||||
"hybrid".to_string()
|
||||
} else if mode == "off" {
|
||||
"compute".to_string()
|
||||
} else {
|
||||
"nvidia".to_string()
|
||||
}
|
||||
} else {
|
||||
info!("No dGPU driver (nouveau or nvidia) loaded");
|
||||
"integrated".to_string()
|
||||
};
|
||||
|
||||
Ok(vendor)
|
||||
}
|
||||
|
||||
pub fn is_switching_prime_modes(vendor: &GfxVendors) -> Result<bool, GfxError> {
|
||||
let prev_mode = GfxVendors::from_str(&Self::get_vendor()?)?;
|
||||
let x = (prev_mode == GfxVendors::Hybrid || prev_mode == GfxVendors::Nvidia)
|
||||
&& (*vendor == GfxVendors::Hybrid || *vendor == GfxVendors::Nvidia);
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
/// Write out config files if required, enable/disable relevant services, and update the ramdisk
|
||||
pub fn set(&mut self, vendor: GfxVendors) -> Result<String, GfxError> {
|
||||
//self.switchable_or_fail()?;
|
||||
|
||||
let mode = if vendor == GfxVendors::Hybrid {
|
||||
"on-demand\n"
|
||||
} else if vendor == GfxVendors::Nvidia {
|
||||
"on\n"
|
||||
} else {
|
||||
// Integrated or Compute
|
||||
"off\n"
|
||||
};
|
||||
|
||||
info!("Setting {} to {}", PRIME_DISCRETE_PATH, mode);
|
||||
Self::set_prime_discrete(mode)?;
|
||||
|
||||
// Switching from hybrid to/from nvidia shouldn't require a ramdisk update
|
||||
// or a reboot.
|
||||
let no_reboot = Self::is_switching_prime_modes(&vendor)?;
|
||||
|
||||
{
|
||||
info!("Writing {}", MODPROBE_PATH);
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(MODPROBE_PATH)
|
||||
.map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
let text = if vendor == GfxVendors::Hybrid {
|
||||
MODPROBE_HYBRID
|
||||
} else if vendor == GfxVendors::Compute {
|
||||
MODPROBE_COMPUTE
|
||||
} else if vendor == GfxVendors::Nvidia {
|
||||
MODPROBE_NVIDIA
|
||||
} else {
|
||||
MODPROBE_INTEGRATED
|
||||
};
|
||||
|
||||
file.write_all(text)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||
}
|
||||
|
||||
info!("Writing {}", PRIMARY_GPU_XORG_PATH);
|
||||
|
||||
// begin section for non-separated Nvidia xorg modules
|
||||
// eg, not put in their own directory
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(PRIMARY_GPU_XORG_PATH)
|
||||
.map_err(|err| GfxError::Write(PRIMARY_GPU_XORG_PATH.into(), err))?;
|
||||
|
||||
let text = if vendor == GfxVendors::Nvidia {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
||||
} else {
|
||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_END].concat()
|
||||
};
|
||||
|
||||
file.write_all(&text)
|
||||
.and_then(|_| file.sync_all())
|
||||
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||
|
||||
let action = if vendor == GfxVendors::Nvidia {
|
||||
info!("Enabling nvidia-fallback.service");
|
||||
"enable"
|
||||
} else {
|
||||
info!("Disabling nvidia-fallback.service");
|
||||
"disable"
|
||||
};
|
||||
|
||||
let status = Command::new("systemctl")
|
||||
.arg(action)
|
||||
.arg("nvidia-fallback.service")
|
||||
.status()
|
||||
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
|
||||
|
||||
if !status.success() {
|
||||
// Error is ignored in case this service is removed
|
||||
warn!(
|
||||
"systemctl: {} (ignore warning if service does not exist!)",
|
||||
status
|
||||
);
|
||||
}
|
||||
|
||||
let mut required_action = GfxCtrlAction::None;
|
||||
if !no_reboot {
|
||||
info!("Updating initramfs");
|
||||
if let Some(cmd) = self.initfs_cmd.as_mut() {
|
||||
let status = cmd
|
||||
.status()
|
||||
.map_err(|err| GfxError::Write(format!("{:?}", cmd), err))?;
|
||||
if !status.success() {
|
||||
error!("Ram disk update failed");
|
||||
} else {
|
||||
info!("Successfully updated iniramfs");
|
||||
}
|
||||
}
|
||||
required_action = GfxCtrlAction::Reboot;
|
||||
} else if no_reboot {
|
||||
required_action = GfxCtrlAction::RestartX;
|
||||
}
|
||||
|
||||
Ok(required_action.into())
|
||||
}
|
||||
|
||||
// pub fn get_power(&self) -> Option<bool> {
|
||||
// if self.can_switch() {
|
||||
// return Some(self.nvidia.iter().any(GraphicsDevice::exists));
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
pub fn get_runtime_status() -> Result<String, GfxError> {
|
||||
const PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
|
||||
let buf = std::fs::read_to_string(PATH).map_err(|err| GfxError::Read(PATH.into(), err))?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn set_power(&self, power: bool) -> Result<(), GfxError> {
|
||||
// self.switchable_or_fail()?;
|
||||
|
||||
if power {
|
||||
info!("Enabling graphics power");
|
||||
self.bus
|
||||
.rescan()
|
||||
.map_err(|err| GfxError::Bus("bus rescan error".into(), err))?;
|
||||
} else {
|
||||
info!("Disabling graphics power");
|
||||
|
||||
// Unbind NVIDIA graphics devices and their functions
|
||||
let unbinds = self.nvidia.iter().map(|dev| dev.unbind());
|
||||
|
||||
// Remove NVIDIA graphics devices and their functions
|
||||
let removes = self.nvidia.iter().map(|dev| dev.remove());
|
||||
|
||||
Result::from_iter(unbinds.chain(removes))
|
||||
.map_err(|err| GfxError::Command("device unbind error".into(), err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn auto_power(&self) -> Result<(), GfxError> {
|
||||
let vendor = CtrlGraphics::get_vendor()?;
|
||||
self.set_power(vendor != "integrated")
|
||||
}
|
||||
}
|
||||
30
ctrl-gfx/src/error.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GfxError {
|
||||
ParseVendor,
|
||||
Path(String, std::io::Error),
|
||||
Read(String, std::io::Error),
|
||||
Write(String, std::io::Error),
|
||||
Module(String, std::io::Error),
|
||||
Bus(String, std::io::Error),
|
||||
Command(String, std::io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for GfxError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GfxError::ParseVendor => write!(f, "Could not parse vendor name"),
|
||||
GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
|
||||
GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||
GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||
GfxError::Module(func, error) => write!(f, "Module error: {}: {}", func, error),
|
||||
GfxError::Bus(func, error) => write!(f, "Bus error: {}: {}", func, error),
|
||||
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for GfxError {}
|
||||
58
ctrl-gfx/src/lib.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
pub mod vendors;
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub mod ctrl_gfx;
|
||||
|
||||
pub mod system;
|
||||
|
||||
const PRIME_DISCRETE_PATH: &str = "/etc/prime-discrete";
|
||||
const MODPROBE_PATH: &str = "/etc/modprobe.d/asusd.conf";
|
||||
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
|
||||
//const DRACUT_PATH: &str = "/usr/bin/dracut";
|
||||
|
||||
static MODPROBE_NVIDIA: &[u8] = MODPROBE_HYBRID;
|
||||
|
||||
static MODPROBE_HYBRID: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
alias i2c_nvidia_gpu off
|
||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
||||
options nvidia-drm modeset=1
|
||||
"#;
|
||||
|
||||
static MODPROBE_COMPUTE: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
alias i2c_nvidia_gpu off
|
||||
options nvidia NVreg_DynamicPowerManagement=0x02
|
||||
options nvidia-drm modeset=0
|
||||
"#;
|
||||
|
||||
static MODPROBE_INTEGRATED: &[u8] = br#"# Automatically generated by asusd
|
||||
blacklist i2c_nvidia_gpu
|
||||
blacklist nouveau
|
||||
blacklist nvidia
|
||||
blacklist nvidia-drm
|
||||
blacklist nvidia-modeset
|
||||
alias i2c_nvidia_gpu off
|
||||
alias nouveau off
|
||||
alias nvidia off
|
||||
alias nvidia-drm off
|
||||
alias nvidia-modeset off
|
||||
"#;
|
||||
|
||||
const PRIMARY_GPU_XORG_PATH: &str = "/etc/X11/xorg.conf.d/90-nvidia-primary.conf";
|
||||
|
||||
static PRIMARY_GPU_BEGIN: &[u8] = br#"# Automatically generated by asusd
|
||||
Section "OutputClass"
|
||||
Identifier "nvidia"
|
||||
MatchDriver "nvidia-drm"
|
||||
Driver "nvidia"
|
||||
Option "AllowEmptyInitialConfiguration"
|
||||
Option "AllowExternalGpus""#;
|
||||
|
||||
static PRIMARY_GPU_NVIDIA: &[u8] = br#"
|
||||
Option "PrimaryGPU" "true""#;
|
||||
|
||||
static PRIMARY_GPU_END: &[u8] = br#"
|
||||
EndSection"#;
|
||||
|
||||
127
ctrl-gfx/src/system.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use log::{error, info, warn};
|
||||
use std::fs::read_to_string;
|
||||
use std::{fs::write, io, path::PathBuf};
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
fn parse(line: &str) -> io::Result<Module> {
|
||||
let mut parts = line.split(' ');
|
||||
|
||||
let name = parts
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "module name not found"))?;
|
||||
|
||||
Ok(Module {
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all() -> io::Result<Vec<Module>> {
|
||||
let mut modules = Vec::new();
|
||||
|
||||
let data = read_to_string("/proc/modules")?;
|
||||
for line in data.lines() {
|
||||
let module = Module::parse(line)?;
|
||||
modules.push(module);
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciBus {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl PciBus {
|
||||
pub fn new() -> io::Result<PciBus> {
|
||||
let path = PathBuf::from("/sys/bus/pci");
|
||||
if path.is_dir() {
|
||||
Ok(PciBus { path })
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"pci directory not found",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rescan(&self) -> io::Result<()> {
|
||||
write(self.path.join("rescan"), "1")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphicsDevice {
|
||||
id: String,
|
||||
functions: Vec<PciDevice>,
|
||||
}
|
||||
|
||||
impl GraphicsDevice {
|
||||
pub fn new(id: String, functions: Vec<PciDevice>) -> GraphicsDevice {
|
||||
GraphicsDevice { id, functions }
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> bool {
|
||||
self.functions.iter().any(|func| func.path().exists())
|
||||
}
|
||||
|
||||
pub fn unbind(&self) -> Result<(), std::io::Error> {
|
||||
for func in self.functions.iter() {
|
||||
if func.path().exists() {
|
||||
match func.driver() {
|
||||
Ok(driver) => {
|
||||
info!("{}: Unbinding {}", driver.id(), func.id());
|
||||
unsafe {
|
||||
driver.unbind(&func).map_err(|err| {
|
||||
error!("gfx unbind: {}", err);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Err(err) => match err.kind() {
|
||||
io::ErrorKind::NotFound => (),
|
||||
_ => {
|
||||
error!("gfx driver: {:?}, {}", func.path(), err);
|
||||
return Err(err);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove(&self) -> Result<(), std::io::Error> {
|
||||
for func in self.functions.iter() {
|
||||
if func.path().exists() {
|
||||
match func.driver() {
|
||||
Ok(driver) => {
|
||||
error!("{}: in use by {}", func.id(), driver.id());
|
||||
}
|
||||
Err(why) => match why.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
info!("{}: Removing", func.id());
|
||||
unsafe {
|
||||
// ignore errors and carry on
|
||||
if let Err(err) = func.remove() {
|
||||
error!("gfx remove: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Remove device failed");
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
warn!("{}: Already removed", func.id());
|
||||
}
|
||||
}
|
||||
info!("Removed all gfx devices");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
85
ctrl-gfx/src/vendors.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum GfxVendors {
|
||||
Nvidia,
|
||||
Integrated,
|
||||
Compute,
|
||||
Hybrid,
|
||||
}
|
||||
|
||||
use crate::error::GfxError;
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromStr for GfxVendors {
|
||||
type Err = GfxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"nvidia" => Ok(GfxVendors::Nvidia),
|
||||
"hybrid" => Ok(GfxVendors::Hybrid),
|
||||
"compute" => Ok(GfxVendors::Compute),
|
||||
"integrated" => Ok(GfxVendors::Integrated),
|
||||
"nvidia\n" => Ok(GfxVendors::Nvidia),
|
||||
"hybrid\n" => Ok(GfxVendors::Hybrid),
|
||||
"compute\n" => Ok(GfxVendors::Compute),
|
||||
"integrated\n" => Ok(GfxVendors::Integrated),
|
||||
_ => Err(GfxError::ParseVendor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxVendors> for &str {
|
||||
fn from(mode: &GfxVendors) -> Self {
|
||||
match mode {
|
||||
GfxVendors::Nvidia => "nvidia",
|
||||
GfxVendors::Hybrid => "hybrid",
|
||||
GfxVendors::Compute => "compute",
|
||||
GfxVendors::Integrated => "integrated",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GfxCtrlAction {
|
||||
Reboot,
|
||||
RestartX,
|
||||
None,
|
||||
}
|
||||
|
||||
impl FromStr for GfxCtrlAction {
|
||||
type Err = GfxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"reboot" => Ok(GfxCtrlAction::Reboot),
|
||||
"restartx" => Ok(GfxCtrlAction::RestartX),
|
||||
"none" => Ok(GfxCtrlAction::None),
|
||||
_ => Err(GfxError::ParseVendor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxCtrlAction> for &str {
|
||||
fn from(mode: &GfxCtrlAction) -> Self {
|
||||
match mode {
|
||||
GfxCtrlAction::Reboot => "reboot",
|
||||
GfxCtrlAction::RestartX => "restartx",
|
||||
GfxCtrlAction::None => "none",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&GfxCtrlAction> for String {
|
||||
fn from(mode: &GfxCtrlAction) -> Self {
|
||||
match mode {
|
||||
GfxCtrlAction::Reboot => "reboot".into(),
|
||||
GfxCtrlAction::RestartX => "restartx".into(),
|
||||
GfxCtrlAction::None => "none".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GfxCtrlAction> for String {
|
||||
fn from(mode: GfxCtrlAction) -> Self {
|
||||
(&mode).into()
|
||||
}
|
||||
}
|
||||
7
data/90-asusd-nvidia-pm.rules
Normal file
@@ -0,0 +1,7 @@
|
||||
# Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
|
||||
ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
|
||||
|
||||
# Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
|
||||
ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
|
||||
4
data/90-nvidia-screen-G05.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
Section "ServerLayout"
|
||||
Identifier "layout"
|
||||
Option "AllowNVIDIAGPUScreens"
|
||||
EndSection
|
||||
14
data/asus-notify.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=ASUS Notifications
|
||||
StartLimitInterval=200
|
||||
StartLimitBurst=2
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/asus-notify
|
||||
Restart=on-failure
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
Type=simple
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
12
data/asusd-alt.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=ASUS Notebook Control
|
||||
After=basic.target syslog.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/asusd
|
||||
Restart=on-failure
|
||||
Type=dbus
|
||||
BusName=org.asuslinux.Daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -3,6 +3,11 @@ 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 = "Zephyrus"
|
||||
board_names = ["GM501GM", "GX531"]
|
||||
|
||||
BIN
data/icons/asus_notif_blue.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
data/icons/asus_notif_green.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
data/icons/asus_notif_orange.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
data/icons/asus_notif_red.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
data/icons/asus_notif_white.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
data/icons/asus_notif_yellow.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
@@ -1,338 +0,0 @@
|
||||
From 61e7ffe80b12db8ebb6033cfe2b70cccaefc07e1 Mon Sep 17 00:00:00 2001
|
||||
From: Luke D Jones <luke@ljones.dev>
|
||||
Date: Sat, 1 Aug 2020 20:33:28 +1200
|
||||
Subject: [PATCH] HID: asus: add support for ASUS N-Key keyboard
|
||||
|
||||
Enable missing functionality of the keyboard found in many ASUS
|
||||
Zephyrus laptops: Fn key combos and hotkeys, keyboard backlight
|
||||
brightness control, and notify asus-wmi to toggle "fan-mode".
|
||||
Two input event codes are added for keyboard LED mode switching
|
||||
prev/next.
|
||||
|
||||
The keyboard has many of the same key outputs as the existing G752
|
||||
keyboard including a few extras, and varies a little between laptop
|
||||
models.
|
||||
|
||||
Additionally the keyboard requires the LED interface to be
|
||||
intitialised before such things as keyboard backlight control work.
|
||||
|
||||
Misc changes in scope: update some hardcoded comparisons to use an
|
||||
available define, change "Mic Toggle" to use a keycode that works.
|
||||
|
||||
Signed-off-by: Luke D Jones <luke@ljones.dev>
|
||||
---
|
||||
drivers/hid/hid-asus.c | 164 ++++++++++++++++++---
|
||||
drivers/hid/hid-ids.h | 1 +
|
||||
include/linux/platform_data/x86/asus-wmi.h | 2 +
|
||||
include/uapi/linux/input-event-codes.h | 7 +
|
||||
4 files changed, 152 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
||||
index e6e4c841fb06..fa9928672110 100644
|
||||
--- a/drivers/hid/hid-asus.c
|
||||
+++ b/drivers/hid/hid-asus.c
|
||||
@@ -26,10 +26,12 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <linux/platform_data/x86/asus-wmi.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
|
||||
#include <linux/power_supply.h>
|
||||
+#include <../drivers/platform/x86/asus-wmi.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@@ -46,6 +48,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
#define FEATURE_KBD_REPORT_ID 0x5a
|
||||
#define FEATURE_KBD_REPORT_SIZE 16
|
||||
+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
||||
+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
||||
|
||||
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
||||
|
||||
@@ -77,6 +81,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_G752_KEYBOARD BIT(8)
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
#define QUIRK_T90CHI BIT(10)
|
||||
+#define QUIRK_ROG_NKEY_KEYBOARD BIT(12)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -257,10 +262,33 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This enables triggering events in asus-wmi
|
||||
+*/
|
||||
+static int asus_wmi_send_event(struct asus_drvdata *drvdat, u8 code)
|
||||
+{
|
||||
+ int err;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
|
||||
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 0) {
|
||||
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
|
||||
(usage->hid & HID_USAGE) != 0x00 &&
|
||||
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
|
||||
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
|
||||
@@ -281,6 +309,20 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
if (drvdata->tp && data[0] == INPUT_REPORT_ID)
|
||||
return asus_report_input(drvdata, data, size);
|
||||
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /*
|
||||
+ * Skip these report ID, the device emits a continuous stream associated
|
||||
+ * with the AURA mode it is in
|
||||
+ */
|
||||
+ if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
|
||||
+ report->id == FEATURE_KBD_LED_REPORT_ID2) {
|
||||
+ return -1;
|
||||
+ /* Fn+F5 "fan" symbol, trigger WMI event to toggle next mode */
|
||||
+ } else if (report->id == FEATURE_KBD_REPORT_ID && data[1] == 0xae) {
|
||||
+ return asus_wmi_send_event(drvdata, 0xae);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -293,7 +335,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
|
||||
+ // The report ID should be set from the incoming buffer due to LED and key
|
||||
+ // interfaces having different pages
|
||||
+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
@@ -346,6 +390,44 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int asus_kbd_led_init(struct hid_device *hdev)
|
||||
+{
|
||||
+ u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
|
||||
+ u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
|
||||
+ 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
|
||||
+ u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
|
||||
+ 0x05, 0x20, 0x31, 0x00, 0x08 };
|
||||
+ int ret;
|
||||
+
|
||||
+ hid_warn(hdev, "Asus initialise N-KEY Device");
|
||||
+ /* The first message is an init start */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init start command: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.0: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.1: %d\n", ret);
|
||||
+
|
||||
+ /* begin second report ID with same data */
|
||||
+ buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+ buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.0: %d\n", ret);
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.1: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -409,19 +491,28 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
- /* Initialize keyboard */
|
||||
- ret = asus_kbd_init(hdev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ /* Initialize keyboard */
|
||||
+ ret = asus_kbd_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Get keyboard functions */
|
||||
- ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ /* Get keyboard functions */
|
||||
+ ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Check for backlight support */
|
||||
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
- return -ENODEV;
|
||||
+ /* Check for backlight support */
|
||||
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /* Initialize keyboard LED interface and Vendor keys on 0x1866 */
|
||||
+ ret = asus_kbd_led_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct asus_kbd_leds),
|
||||
@@ -693,14 +784,14 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
}
|
||||
|
||||
/* ASUS-specific keyboard hotkeys */
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
|
||||
set_bit(EV_REP, hi->input->evbit);
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
|
||||
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
|
||||
- case 0x7c: asus_map_key_clear(KEY_MICMUTE); break;
|
||||
+ case 0x7c: asus_map_key_clear(KEY_F20); break;
|
||||
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
|
||||
case 0xb5: asus_map_key_clear(KEY_CALC); break;
|
||||
@@ -713,16 +804,42 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
/* ROG key */
|
||||
case 0x38: asus_map_key_clear(KEY_PROG1); break;
|
||||
|
||||
- /* Fn+C ASUS Splendid */
|
||||
- case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
+ default:
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+C ASUS Splendid */
|
||||
+ case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
|
||||
- /* Fn+Space Power4Gear Hybrid */
|
||||
- case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
+ /* Fn+Space Power4Gear Hybrid */
|
||||
+ case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
- /* Fn+F5 "fan" symbol on FX503VD */
|
||||
- case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+ /* Fn+F5 "fan" symbol on FX503VD */
|
||||
+ case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* device 0x1866, N-KEY Device specific */
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+Ret "Calc" symbol on device 0x1866, N-KEY Device */
|
||||
+ case 0x92: asus_map_key_clear(KEY_CALC); break;
|
||||
+
|
||||
+ /* Fn+Left Aura mode previous */
|
||||
+ case 0xb2: asus_map_key_clear(KEY_KBDILLUM_MODE_PREV); break;
|
||||
+
|
||||
+ /* Fn+Right Aura mode next */
|
||||
+ case 0xb3: asus_map_key_clear(KEY_KBDILLUM_MODE_NEXT); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
return -1;
|
||||
@@ -1043,6 +1160,9 @@ static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
|
||||
index 1c71a1aa76b2..42c2ca3832e0 100644
|
||||
--- a/drivers/hid/hid-ids.h
|
||||
+++ b/drivers/hid/hid-ids.h
|
||||
@@ -193,6 +193,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index d39fc658c320..10fca778ff9c 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -27,6 +27,8 @@
|
||||
#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
|
||||
#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
|
||||
|
||||
+#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method ?? */
|
||||
+
|
||||
#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
|
||||
|
||||
/* Wireless */
|
||||
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
|
||||
index b6a835d37826..928abcf020d5 100644
|
||||
--- a/include/uapi/linux/input-event-codes.h
|
||||
+++ b/include/uapi/linux/input-event-codes.h
|
||||
@@ -772,6 +772,13 @@
|
||||
#define BTN_TRIGGER_HAPPY39 0x2e6
|
||||
#define BTN_TRIGGER_HAPPY40 0x2e7
|
||||
|
||||
+/*
|
||||
+ * Some keyboards have function keys associated with
|
||||
+ * changing the keyboard backlight modes, e.g, RGB patterns
|
||||
+ */
|
||||
+#define KEY_KBDILLUM_MODE_PREV 0x2ea
|
||||
+#define KEY_KBDILLUM_MODE_NEXT 0x2eb
|
||||
+
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
#define KEY_MIN_INTERESTING KEY_MUTE
|
||||
#define KEY_MAX 0x2ff
|
||||
--
|
||||
2.26.2
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
From 2b70c3daf1bd92f9163efb726e37fb3e0bcc8989 Mon Sep 17 00:00:00 2001
|
||||
From: Luke D Jones <luke@ljones.dev>
|
||||
Date: Thu, 30 Jul 2020 16:51:06 +1200
|
||||
Subject: [PATCH] HID: asus: add support for ASUS N-Key keyboard
|
||||
|
||||
Enable missing functionality of the keyboard found in many ASUS
|
||||
Zephyrus laptops: Fn key combos and hotkeys, keyboard backlight
|
||||
brightness control, and notify asus-wmi to toggle "fan-mode".
|
||||
Two input event codes are added for keyboard LED mode switching
|
||||
prev/next.
|
||||
|
||||
The keyboard has many of the same key outputs as the existing G752
|
||||
keyboard including a few extras, and varies a little between laptop
|
||||
models.
|
||||
|
||||
Additionally the keyboard requires the LED interface to be
|
||||
intitialised before such things as keyboard backlight control work.
|
||||
|
||||
Misc changes in scope: update some hardcoded comparisons to use an
|
||||
available define, change "Mic Toggle" to use a keycode that works.
|
||||
|
||||
Signed-off-by: Luke D Jones <luke@ljones.dev>
|
||||
---
|
||||
drivers/hid/hid-asus.c | 166 ++++++++++++++++++---
|
||||
drivers/hid/hid-ids.h | 1 +
|
||||
include/linux/platform_data/x86/asus-wmi.h | 2 +
|
||||
include/uapi/linux/input-event-codes.h | 7 +
|
||||
4 files changed, 153 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
|
||||
index c183caf89d49..5dfca90cd616 100644
|
||||
--- a/drivers/hid/hid-asus.c
|
||||
+++ b/drivers/hid/hid-asus.c
|
||||
@@ -26,10 +26,12 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/acpi.h>
|
||||
#include <linux/platform_data/x86/asus-wmi.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
|
||||
#include <linux/power_supply.h>
|
||||
+#include <../drivers/platform/x86/asus-wmi.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
@@ -48,6 +50,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define INPUT_REPORT_ID 0x5d
|
||||
#define FEATURE_KBD_REPORT_ID 0x5a
|
||||
#define FEATURE_KBD_REPORT_SIZE 16
|
||||
+#define FEATURE_KBD_LED_REPORT_ID1 0x5d
|
||||
+#define FEATURE_KBD_LED_REPORT_ID2 0x5e
|
||||
|
||||
#define SUPPORT_KBD_BACKLIGHT BIT(0)
|
||||
|
||||
@@ -80,6 +84,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
#define QUIRK_T90CHI BIT(10)
|
||||
#define QUIRK_MEDION_E1239T BIT(11)
|
||||
+#define QUIRK_ROG_NKEY_KEYBOARD BIT(12)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
@@ -305,10 +310,33 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * This enables triggering events in asus-wmi
|
||||
+ */
|
||||
+static int asus_wmi_send_event(struct asus_drvdata *drvdat, u8 code)
|
||||
+{
|
||||
+ int err;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
|
||||
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 0) {
|
||||
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
|
||||
(usage->hid & HID_USAGE) != 0x00 &&
|
||||
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
|
||||
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
|
||||
@@ -332,6 +360,20 @@ static int asus_raw_event(struct hid_device *hdev,
|
||||
if (drvdata->quirks & QUIRK_MEDION_E1239T)
|
||||
return asus_e1239t_event(drvdata, data, size);
|
||||
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /*
|
||||
+ * Skip these report ID, the device emits a continuous stream associated
|
||||
+ * with the AURA mode it is in
|
||||
+ */
|
||||
+ if (report->id == FEATURE_KBD_LED_REPORT_ID1 ||
|
||||
+ report->id == FEATURE_KBD_LED_REPORT_ID2) {
|
||||
+ return -1;
|
||||
+ /* Fn+F5 "fan" symbol, trigger WMI event to toggle next mode */
|
||||
+ } else if (report->id == FEATURE_KBD_REPORT_ID && data[1] == 0xae) {
|
||||
+ return asus_wmi_send_event(drvdata, 0xae);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -344,7 +386,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size
|
||||
if (!dmabuf)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, dmabuf,
|
||||
+ // The report ID should be set from the incoming buffer due to LED and key
|
||||
+ // interfaces having different pages
|
||||
+ ret = hid_hw_raw_request(hdev, buf[0], dmabuf,
|
||||
buf_size, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(dmabuf);
|
||||
@@ -397,6 +441,44 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int asus_kbd_led_init(struct hid_device *hdev)
|
||||
+{
|
||||
+ u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 };
|
||||
+ u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20,
|
||||
+ 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
|
||||
+ u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1,
|
||||
+ 0x05, 0x20, 0x31, 0x00, 0x08 };
|
||||
+ int ret;
|
||||
+
|
||||
+ hid_warn(hdev, "Asus initialise N-KEY Device");
|
||||
+ /* The first message is an init start */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init start command: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.0: %d\n", ret);
|
||||
+ /* Followed by a string */
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 1.1: %d\n", ret);
|
||||
+
|
||||
+ /* begin second report ID with same data */
|
||||
+ buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+ buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2;
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.0: %d\n", ret);
|
||||
+
|
||||
+ ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3));
|
||||
+ if (ret < 0)
|
||||
+ hid_err(hdev, "Asus failed to send init command 2.1: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -460,19 +542,28 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
|
||||
unsigned char kbd_func;
|
||||
int ret;
|
||||
|
||||
- /* Initialize keyboard */
|
||||
- ret = asus_kbd_init(hdev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ /* Initialize keyboard */
|
||||
+ ret = asus_kbd_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Get keyboard functions */
|
||||
- ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ /* Get keyboard functions */
|
||||
+ ret = asus_kbd_get_functions(hdev, &kbd_func);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
|
||||
- /* Check for backlight support */
|
||||
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
- return -ENODEV;
|
||||
+ /* Check for backlight support */
|
||||
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ /* Initialize keyboard LED interface and Vendor keys on 0x1866 */
|
||||
+ ret = asus_kbd_led_init(hdev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct asus_kbd_leds),
|
||||
@@ -751,14 +842,14 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)))
|
||||
return -1;
|
||||
|
||||
- /* ASUS-specific keyboard hotkeys */
|
||||
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
|
||||
+ /* ASUS-specific keyboard hotkeys and led backlight */
|
||||
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR) {
|
||||
switch (usage->hid & HID_USAGE) {
|
||||
case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
|
||||
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
|
||||
- case 0x7c: asus_map_key_clear(KEY_MICMUTE); break;
|
||||
+ case 0x7c: asus_map_key_clear(KEY_F20); break;
|
||||
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
|
||||
case 0xb5: asus_map_key_clear(KEY_CALC); break;
|
||||
@@ -771,16 +862,42 @@ static int asus_input_mapping(struct hid_device *hdev,
|
||||
/* ROG key */
|
||||
case 0x38: asus_map_key_clear(KEY_PROG1); break;
|
||||
|
||||
- /* Fn+C ASUS Splendid */
|
||||
- case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
+ default:
|
||||
+ if (drvdata->quirks & QUIRK_G752_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+C ASUS Splendid */
|
||||
+ case 0xba: asus_map_key_clear(KEY_PROG2); break;
|
||||
|
||||
- /* Fn+Space Power4Gear Hybrid */
|
||||
- case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
+ /* Fn+Space Power4Gear Hybrid */
|
||||
+ case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
- /* Fn+F5 "fan" symbol on FX503VD */
|
||||
- case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+ /* Fn+F5 "fan" symbol on FX503VD */
|
||||
+ case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* device 0x1866, N-KEY Device specific */
|
||||
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
|
||||
+ switch (usage->hid & HID_USAGE) {
|
||||
+ /* Fn+Ret "Calc" symbol on device 0x1866, N-KEY Device */
|
||||
+ case 0x92: asus_map_key_clear(KEY_CALC); break;
|
||||
+
|
||||
+ /* Fn+Left Aura mode previous */
|
||||
+ case 0xb2: asus_map_key_clear(KEY_KBDILLUM_MODE_PREV); break;
|
||||
+
|
||||
+ /* Fn+Right Aura mode next */
|
||||
+ case 0xb3: asus_map_key_clear(KEY_KBDILLUM_MODE_NEXT); break;
|
||||
+
|
||||
+ default:
|
||||
+ return -1;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
return -1;
|
||||
@@ -1126,6 +1243,9 @@ static const struct hid_device_id asus_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
|
||||
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
|
||||
index 6f370e020feb..c9f930ddcfd7 100644
|
||||
--- a/drivers/hid/hid-ids.h
|
||||
+++ b/drivers/hid/hid-ids.h
|
||||
@@ -190,6 +190,7 @@
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
+#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index 897b8332a39f..05253cfe786c 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -27,6 +27,8 @@
|
||||
#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */
|
||||
#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */
|
||||
|
||||
+#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method ?? */
|
||||
+
|
||||
#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE
|
||||
|
||||
/* Wireless */
|
||||
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
|
||||
index 0c2e27d28e0a..ca59f7d7a25e 100644
|
||||
--- a/include/uapi/linux/input-event-codes.h
|
||||
+++ b/include/uapi/linux/input-event-codes.h
|
||||
@@ -772,6 +772,13 @@
|
||||
#define BTN_TRIGGER_HAPPY39 0x2e6
|
||||
#define BTN_TRIGGER_HAPPY40 0x2e7
|
||||
|
||||
+/*
|
||||
+ * Some keyboards have function keys associated with
|
||||
+ * changing the keyboard backlight modes, e.g, RGB patterns
|
||||
+ */
|
||||
+#define KEY_KBDILLUM_MODE_PREV 0x2ea
|
||||
+#define KEY_KBDILLUM_MODE_NEXT 0x2eb
|
||||
+
|
||||
/* We avoid low common keys in module aliases so they don't get huge. */
|
||||
#define KEY_MIN_INTERESTING KEY_MUTE
|
||||
#define KEY_MAX 0x2ff
|
||||
--
|
||||
2.26.2
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
---
|
||||
.../ABI/testing/sysfs-platform-asus-wmi | 10 ++
|
||||
drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++
|
||||
include/linux/platform_data/x86/asus-wmi.h | 1 +
|
||||
3 files changed, 124 insertions(+)
|
||||
|
||||
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
index 9e99f2909612..1efac0ddb417 100644
|
||||
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
|
||||
@@ -46,3 +46,13 @@ Description:
|
||||
* 0 - normal,
|
||||
* 1 - overboost,
|
||||
* 2 - silent
|
||||
+
|
||||
+What: /sys/devices/platform/<platform>/throttle_thermal_policy
|
||||
+Date: Dec 2019
|
||||
+KernelVersion: 5.6
|
||||
+Contact: "Leonid Maksymchuk" <leonmaxx@gmail.com>
|
||||
+Description:
|
||||
+ Throttle thermal policy mode:
|
||||
+ * 0 - default,
|
||||
+ * 1 - overboost,
|
||||
+ * 2 - silent
|
||||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
||||
index 821b08e01635..f10ec9d745e5 100644
|
||||
--- a/drivers/platform/x86/asus-wmi.c
|
||||
+++ b/drivers/platform/x86/asus-wmi.c
|
||||
@@ -61,6 +61,7 @@ MODULE_LICENSE("GPL");
|
||||
#define NOTIFY_KBD_BRTDWN 0xc5
|
||||
#define NOTIFY_KBD_BRTTOGGLE 0xc7
|
||||
#define NOTIFY_KBD_FBM 0x99
|
||||
+#define NOTIFY_KBD_TTP 0xae
|
||||
|
||||
#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
|
||||
|
||||
@@ -81,6 +82,10 @@ MODULE_LICENSE("GPL");
|
||||
#define ASUS_FAN_BOOST_MODE_SILENT_MASK 0x02
|
||||
#define ASUS_FAN_BOOST_MODES_MASK 0x03
|
||||
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT 0
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1
|
||||
+#define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2
|
||||
+
|
||||
#define USB_INTEL_XUSB2PR 0xD0
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
|
||||
|
||||
@@ -198,6 +203,9 @@ struct asus_wmi {
|
||||
u8 fan_boost_mode_mask;
|
||||
u8 fan_boost_mode;
|
||||
|
||||
+ bool throttle_thermal_policy_available;
|
||||
+ u8 throttle_thermal_policy_mode;
|
||||
+
|
||||
// The RSOC controls the maximum charging percentage.
|
||||
bool battery_rsoc_available;
|
||||
|
||||
@@ -1724,6 +1732,98 @@ static ssize_t fan_boost_mode_store(struct device *dev,
|
||||
// Fan boost mode: 0 - normal, 1 - overboost, 2 - silent
|
||||
static DEVICE_ATTR_RW(fan_boost_mode);
|
||||
|
||||
+/* Throttle thermal policy ****************************************************/
|
||||
+
|
||||
+static int throttle_thermal_policy_check_present(struct asus_wmi *asus)
|
||||
+{
|
||||
+ u32 result;
|
||||
+ int err;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_available = false;
|
||||
+
|
||||
+ err = asus_wmi_get_devstate(asus,
|
||||
+ ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
+ &result);
|
||||
+ if (err) {
|
||||
+ if (err == -ENODEV)
|
||||
+ return 0;
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
|
||||
+ asus->throttle_thermal_policy_available = true;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
+{
|
||||
+ int err;
|
||||
+ u8 value;
|
||||
+ u32 retval;
|
||||
+
|
||||
+ value = asus->throttle_thermal_policy_mode;
|
||||
+
|
||||
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
|
||||
+ value, &retval);
|
||||
+ if (err) {
|
||||
+ pr_warn("Failed to set throttle thermal policy: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != 1) {
|
||||
+ pr_warn("Failed to set throttle thermal policy (retval): 0x%x\n",
|
||||
+ retval);
|
||||
+ return -EIO;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
|
||||
+{
|
||||
+ u8 new_mode = asus->throttle_thermal_policy_mode + 1;
|
||||
+
|
||||
+ if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
+ new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = new_mode;
|
||||
+ return throttle_thermal_policy_write(asus);
|
||||
+}
|
||||
+
|
||||
+static ssize_t throttle_thermal_policy_show(struct device *dev,
|
||||
+ struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
+ u8 mode = asus->throttle_thermal_policy_mode;
|
||||
+
|
||||
+ return scnprintf(buf, PAGE_SIZE, "%d\n", mode);
|
||||
+}
|
||||
+
|
||||
+static ssize_t throttle_thermal_policy_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t count)
|
||||
+{
|
||||
+ int result;
|
||||
+ u8 new_mode;
|
||||
+ struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
+
|
||||
+ result = kstrtou8(buf, 10, &new_mode);
|
||||
+ if (result < 0)
|
||||
+ return result;
|
||||
+
|
||||
+ if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = new_mode;
|
||||
+ throttle_thermal_policy_write(asus);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent
|
||||
+static DEVICE_ATTR_RW(throttle_thermal_policy);
|
||||
+
|
||||
/* Backlight ******************************************************************/
|
||||
|
||||
static int read_backlight_power(struct asus_wmi *asus)
|
||||
@@ -2005,6 +2105,11 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) {
|
||||
+ throttle_thermal_policy_switch_next(asus);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
|
||||
return;
|
||||
|
||||
@@ -2155,6 +2260,7 @@ static struct attribute *platform_attributes[] = {
|
||||
&dev_attr_lid_resume.attr,
|
||||
&dev_attr_als_enable.attr,
|
||||
&dev_attr_fan_boost_mode.attr,
|
||||
+ &dev_attr_throttle_thermal_policy.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -2178,6 +2284,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
devid = ASUS_WMI_DEVID_ALS_ENABLE;
|
||||
else if (attr == &dev_attr_fan_boost_mode.attr)
|
||||
ok = asus->fan_boost_mode_available;
|
||||
+ else if (attr == &dev_attr_throttle_thermal_policy.attr)
|
||||
+ ok = asus->throttle_thermal_policy_available;
|
||||
|
||||
if (devid != -1)
|
||||
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
|
||||
@@ -2437,6 +2545,10 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto fail_fan_boost_mode;
|
||||
|
||||
+ err = throttle_thermal_policy_check_present(asus);
|
||||
+ if (err)
|
||||
+ goto fail_throttle_thermal_policy;
|
||||
+
|
||||
err = asus_wmi_sysfs_init(asus->platform_device);
|
||||
if (err)
|
||||
goto fail_sysfs;
|
||||
@@ -2521,6 +2633,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
fail_input:
|
||||
asus_wmi_sysfs_exit(asus->platform_device);
|
||||
fail_sysfs:
|
||||
+fail_throttle_thermal_policy:
|
||||
fail_fan_boost_mode:
|
||||
fail_platform:
|
||||
kfree(asus);
|
||||
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
|
||||
index 60249e22e844..d39fc658c320 100644
|
||||
--- a/include/linux/platform_data/x86/asus-wmi.h
|
||||
+++ b/include/linux/platform_data/x86/asus-wmi.h
|
||||
@@ -58,6 +58,7 @@
|
||||
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
|
||||
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
|
||||
#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018
|
||||
+#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
|
||||
|
||||
/* Misc */
|
||||
#define ASUS_WMI_DEVID_CAMERA 0x00060013
|
||||
--
|
||||
2.24.0
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
drivers/platform/x86/asus-wmi.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
|
||||
index f10ec9d745e5..469f1a852719 100644
|
||||
--- a/drivers/platform/x86/asus-wmi.c
|
||||
+++ b/drivers/platform/x86/asus-wmi.c
|
||||
@@ -1780,6 +1780,15 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int throttle_thermal_policy_set_default(struct asus_wmi *asus)
|
||||
+{
|
||||
+ if (!asus->throttle_thermal_policy_available)
|
||||
+ return 0;
|
||||
+
|
||||
+ asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
|
||||
+ return throttle_thermal_policy_write(asus);
|
||||
+}
|
||||
+
|
||||
static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
|
||||
{
|
||||
u8 new_mode = asus->throttle_thermal_policy_mode + 1;
|
||||
@@ -2548,6 +2557,8 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
err = throttle_thermal_policy_check_present(asus);
|
||||
if (err)
|
||||
goto fail_throttle_thermal_policy;
|
||||
+ else
|
||||
+ throttle_thermal_policy_set_default(asus);
|
||||
|
||||
err = asus_wmi_sysfs_init(asus->platform_device);
|
||||
if (err)
|
||||
--
|
||||
2.24.0
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
[root@rog tmp]# cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
|
||||
[root@rog tmp]# iasl -d dsdt.dat
|
||||
|
||||
Intel ACPI Component Architecture
|
||||
ASL+ Optimizing Compiler/Disassembler version 20200717
|
||||
Copyright (c) 2000 - 2020 Intel Corporation
|
||||
|
||||
File appears to be binary: found 97272 non-ASCII characters, disassembling
|
||||
Binary file appears to be a valid ACPI table, disassembling
|
||||
Input file dsdt.dat, Length 0x45955 (285013) bytes
|
||||
ACPI: DSDT 0x0000000000000000 045955 (v02 _ASUS_ Notebook 01072009 INTL 20160527)
|
||||
Pass 1 parse of [DSDT]
|
||||
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload-495)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
Pass 2 parse of [DSDT]
|
||||
ACPI Error: ^PCI0.LPCB.EC0_.ACNG: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^PCI0.LPCB.EC0.ACNG], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.NLIM: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.NLIM], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPU: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPU], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^^NPCF.PABS: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^^NPCF.PABS], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.CTGP: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.CTGP], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
ACPI Error: ^^PEG0.PEGP.TGPV: Path has too many parent prefixes (^) (20200717/nsaccess-604)
|
||||
Firmware Error (ACPI): Could not resolve symbol [^^PEG0.PEGP.TGPV], AE_NOT_FOUND (20200717/dswload2-479)
|
||||
ACPI Error: AE_NOT_FOUND, During name lookup/catalog (20200717/psobject-372)
|
||||
Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions)
|
||||
|
||||
Parsing completed
|
||||
Warning - Emitting ASL code "External (ESPC)"
|
||||
This is a conflicting declaration with some other declaration within the ASL code.
|
||||
This external declaration may need to be deleted in order to recompile the dsl file.
|
||||
|
||||
Warning - Emitting ASL code "External (PSON)"
|
||||
This is a conflicting declaration with some other declaration within the ASL code.
|
||||
This external declaration may need to be deleted in order to recompile the dsl file.
|
||||
|
||||
Disassembly completed
|
||||
ASL Output: dsdt.dsl - 2006627 bytes
|
||||
[root@rog tmp]#
|
||||
@@ -1,4 +0,0 @@
|
||||
cat /sys/class/dmi/id/product_name:ROG Strix G512LU_G512LU
|
||||
cat /sys/class/dmi/id/product_family: ROG Strix
|
||||
cat /sys/class/dmi/id/board_name:G512LU
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
001:002:002:DESCRIPTOR 1593779817.131018
|
||||
06 31 FF 09 76 A1 01 85 5A 19 00 2A FF 00 15 00
|
||||
26 FF 00 75 08 95 05 81 00 19 00 2A FF 00 15 00
|
||||
26 FF 00 75 08 95 3F B1 00 C0 05 0C 09 01 A1 01
|
||||
85 02 19 00 2A 3C 02 15 00 26 3C 02 75 10 95 02
|
||||
81 00 C0 06 31 FF 09 79 A1 01 85 5D 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 1F 81 00 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 3F 91 00 19 00 2A FF
|
||||
00 15 00 26 FF 00 75 08 95 3F B1 00 C0 06 31 FF
|
||||
09 80 A1 01 85 5E 19 00 2A FF 00 15 00 26 FF 00
|
||||
75 08 95 05 81 00 19 00 2A FF 00 15 00 26 FF 00
|
||||
75 08 95 3F B1 00 C0
|
||||
|
||||
001:002:001:DESCRIPTOR 1593779817.133253
|
||||
05 01 09 06 A1 01 85 09 75 01 95 08 05 07 19 E0
|
||||
29 E7 15 00 25 01 81 02 95 08 75 01 81 03 95 05
|
||||
75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03
|
||||
95 F0 75 01 05 07 19 00 29 EF 15 00 25 01 81 02
|
||||
C0
|
||||
|
||||
001:002:000:DESCRIPTOR 1593779817.134955
|
||||
05 01 09 06 A1 01 85 01 75 01 95 08 05 07 19 E0
|
||||
29 E7 15 00 25 01 81 02 95 01 75 08 81 03 95 05
|
||||
75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 03
|
||||
95 06 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00
|
||||
81 00 95 C0 75 01 05 07 19 00 29 EF 15 00 25 01
|
||||
81 02 C0
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Product name: ROG Strix G531GT_G531GT
|
||||
Product family: ROG Strix
|
||||
Board name: G531GT
|
||||
|
||||
lsusb | grep 0b05: Bus 001 Device 004: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
1b00a00964188090ffff00000000090000020002000401400000005dbc00010101001000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000 ROGKEY
|
||||
1b006079b90e8090ffff00000000090000020002000401400000005dbc00010101601000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ENTER
|
||||
1b00e014ee088090ffff00000000090000020002000401400000005dbc00010101701000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000 RSHIFT
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 188 KiB |
@@ -1,6 +0,0 @@
|
||||
1b00a0e94d068090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000 LED6
|
||||
1b0020d77e048090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000 LED5
|
||||
1b008089e1048090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000 LED4
|
||||
1b0010103d088090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000 LED3
|
||||
1b007079ce1f8090ffff00000000090000020002000401400000005dbc00010400000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 LED2
|
||||
1b00e0e4fe078090ffff00000000090000020002000401400000005dbc00010400000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 LED1
|
||||
|
Before Width: | Height: | Size: 3.2 MiB |
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1,35 +0,0 @@
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x09, // Report ID (9)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x08, // Usage Page (LEDs)
|
||||
0x19, 0x01, // Usage Minimum (Num Lock)
|
||||
0x29, 0x05, // Usage Maximum (Kana)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0xF0, // Report Count (-16)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x29, 0xEF, // Usage Maximum (0xEF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 65 bytes
|
||||
@@ -1,77 +0,0 @@
|
||||
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
|
||||
0x09, 0x76, // Usage (0x76)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x5A, // Report ID (90)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x3F, // Report Count (63)
|
||||
0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x02, // Report ID (2)
|
||||
0x19, 0x00, // Usage Minimum (Unassigned)
|
||||
0x2A, 0x3C, 0x02, // Usage Maximum (AC Format)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0x3C, 0x02, // Logical Maximum (572)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
|
||||
0x09, 0x79, // Usage (0x79)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x5D, // Report ID (93)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x1F, // Report Count (31)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x3F, // Report Count (63)
|
||||
0x91, 0x00, // Output (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x3F, // Report Count (63)
|
||||
0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined 0xFF31)
|
||||
0x09, 0x80, // Usage (0x80)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x5E, // Report ID (94)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x3F, // Report Count (63)
|
||||
0xB1, 0x00, // Feature (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x08, // Usage Page (LEDs)
|
||||
0x19, 0x01, // Usage Minimum (Num Lock)
|
||||
0x29, 0x05, // Usage Maximum (Kana)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x2A, 0xFF, 0x00, // Usage Maximum (0xFF)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0xC0, // Report Count (-64)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x29, 0xEF, // Usage Maximum (0xEF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 83 bytes
|
||||
@@ -1,129 +0,0 @@
|
||||
Bus 003 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
|
||||
@@ -1,7 +0,0 @@
|
||||
cat /sys/class/dmi/id/product_name
|
||||
ROG Zephyrus G15 GA502IU_GA502IU
|
||||
cat /sys/class/dmi/id/product_family
|
||||
ROG Zephyrus G15
|
||||
cat /sys/class/dmi/id/board_name
|
||||
GA502IU
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
(base) will@zephyrusm:~$ lsusb | grep 0b05
|
||||
Bus 001 Device 003: ID 0b05:1866 ASUSTek Computer, Inc. N-KEY Device
|
||||
(base) will@zephyrusm:~$
|
||||
|
||||
(base) will@zephyrusm:~$ cat /sys/class/dmi/id/product_name
|
||||
Zephyrus M GM501GM
|
||||
(base) will@zephyrusm:~$ cat /sys/class/dmi/id/product_family
|
||||
Zephyrus M
|
||||
(base) will@zephyrusm:~$ cat /sys/class/dmi/id/board_name
|
||||
GM501GM
|
||||
(base) will@zephyrusm:~$
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
001:003:002:DESCRIPTOR 1588963579.696552
|
||||
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:003:001:DESCRIPTOR 1588963579.697560
|
||||
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:003:000:DESCRIPTOR 1588963579.698493
|
||||
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 1E 75 08 15 00 26 FF 00 05 07 19 00 2A FF 00
|
||||
81 00 C0
|
||||
|
||||