DEV Community

Alessandro Vezzoli
Alessandro Vezzoli

Posted on

Full Linux support for ASUS TUF Gaming A16 (FA608WV) — keyboard backlight, brightness, hibernate, all working

If you bought an ASUS TUF Gaming A16 FA608WV (2024/2025, Ryzen AI 9 HX + RTX 4060 Laptop) and ran Linux on it, you probably hit every single one of these walls:

  • Keyboard RGB backlight: completely dead — asusctl, brightnessctl, every command returns success and nothing lights up
  • Screen brightness in hybrid GPU mode: writes succeed in /sys/class/backlight/... but the panel doesn't dim
  • Sleep with the lid closed: laptop overheats in the backpack because the system never actually enters S3/S4

@sureshsaragadam wrote a deep dive on dev.to in January 2026 concluding that everything needed a kernel patch and we just had to wait. There's an open asusctl issue (#700) tracking the same problem.

It turns out every one of these is fixable from userspace today. This post documents the complete chain: how I went from "nothing works" to "everything works" on a clean Pop!_OS 24.04 install, including an upstream-quality asusctl patch and a workaround stack for the screen.

TL;DR

If you have a FA608WV, you can have:

  • ✅ Keyboard RGB controllable from asusctl, rog-control-center, and CLI scripts — color, brightness, modes
  • ✅ Screen brightness Fn keys working in hybrid GPU mode (no need for discrete-GPU mode)
  • ✅ Real S4 hibernate (swapfile-backed), HandleLidSwitch=hibernate, no more backpack-overheating

Skip to the sections that interest you.

Hardware context

Model           ASUS TUF Gaming A16 FA608WV_FA608WV
CPU/iGPU        AMD Ryzen AI 9 HX (Radeon 880M)
dGPU            NVIDIA RTX 4060 (Laptop)
Distro          Pop!_OS 24.04 LTS (single OS, no Windows dual-boot)
Kernel          7.0.11-76070011-generic
BIOS            FA608WV.309 (2025-10-01)
asusctl         6.3.8 (patched, see below)
Enter fullscreen mode Exit fullscreen mode

Part 1 — Keyboard RGB backlight via HID LampArray

The diagnosis

The keyboard on this SKU is NOT behind asus-wmi at all. The legacy /sys/class/leds/asus::kbd_backlight node exists and accepts writes, but the writes go nowhere — the actual backlight controller is an ITE5570 on the I2C-HID bus, vendor 0B05 product 19B6, speaking the Microsoft HID LampArray standard (HID Usage Tables 1.5, Usage Page 0x59).

$ ls /sys/bus/hid/devices/ | grep 0B05
0018:0B05:19B6.0002       # BUS 0018 = I2C-HID, not USB

$ cat /sys/bus/hid/devices/0018:0B05:19B6.0002/uevent
DRIVER=hid-generic
HID_ID=0018:00000B05:000019B6
HID_NAME=ITE5570:00 0B05:19B6
HID_PHYS=i2c-ITE5570:00

$ sudo xxd /sys/bus/hid/devices/0018:0B05:19B6.0002/report_descriptor | grep "05 59"
# match → LampArray Usage Page present
Enter fullscreen mode Exit fullscreen mode

asusctl 6.3.x doesn't probe for HID LampArray devices and only walks USB-side parents. So nothing reaches the chip.

The LampArray protocol is well-defined:

Report ID Name Direction Purpose
0x41 LampArrayAttributes Feature IN LampCount, bounding box, kind, min int.
0x44 LampMultiUpdate Feature OUT Update up to 8 specific lamps
0x45 LampRangeUpdate Feature OUT Update a contiguous range with one RGBI
0x46 LampArrayControl Feature OUT Disable autonomous (vendor) mode

Working sequence:

  1. Disable autonomous mode: HIDIOCSFEATURE with [0x46, 0x00]
  2. Send LampRangeUpdate: [0x45, 0x01, 0x00, 0x00, lamp_count-1, R, G, B, I]

On the FA608WV, LampArrayAttributes reports LampCount=1 (single-zone), Kind=Keyboard, bounding box ~310×144 mm.

Two ways to drive it from Linux

Option A: standalone Python script (fastest)

Doesn't require modifying asusctl. Stand-alone, ~70 lines of stdlib Python. Works immediately.

Save as /usr/local/bin/asus-tuf-kbd-led:

#!/usr/bin/env python3
"""asus-tuf-kbd-led — LampArray controller for ASUS TUF A16 FA608WV (0B05:19B6)."""
import fcntl, struct, sys, glob, json, pathlib

VID, PID = 0x0B05, 0x19B6
STATE_FILE = "/var/lib/asus-tuf-kbd-led/state.json"

def _IOC(d,t,n,s): return (d<<30)|(s<<16)|(t<<8)|n
def HIDIOCSFEATURE(l): return _IOC(3, ord('H'), 0x06, l)
def HIDIOCGFEATURE(l): return _IOC(3, ord('H'), 0x07, l)
def HIDIOCGRAWINFO():  return _IOC(2, ord('H'), 0x03, 8)

RID_ATTRS=0x41; RID_RANGE=0x45; RID_CTRL=0x46

def find_hidraw():
    for path in sorted(glob.glob("/dev/hidraw*")):
        try:
            with open(path, "rb+") as fd:
                buf = bytearray(8)
                fcntl.ioctl(fd, HIDIOCGRAWINFO(), buf, True)
                bus, v, p = struct.unpack("<IHH", bytes(buf))
                if v == VID and p == PID: return path
        except OSError: continue
    return None

# ... (autonomous, range_update, state load/save, dispatch — full script in the gist link below)
Enter fullscreen mode Exit fullscreen mode

udev rule for non-root access:

# /etc/udev/rules.d/99-asus-tuf-lamparray.rules
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", KERNELS=="0018:0B05:19B6.*", \
  MODE="0660", GROUP="input", TAG+="uaccess"
Enter fullscreen mode Exit fullscreen mode

systemd services for boot and resume:

# /etc/systemd/system/asus-tuf-kbd-led.service
[Unit]
Description=ASUS TUF kbd backlight (LampArray restore at boot)
After=multi-user.target

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 1
ExecStart=/usr/local/bin/asus-tuf-kbd-led restore
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode
# /etc/systemd/system/asus-tuf-kbd-led-resume.service
[Unit]
Description=ASUS TUF kbd backlight (LampArray restore after resume)
After=hibernate.target suspend.target hybrid-sleep.target

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 1
ExecStart=/usr/local/bin/asus-tuf-kbd-led restore

[Install]
WantedBy=hibernate.target suspend.target hybrid-sleep.target sleep.target
Enter fullscreen mode Exit fullscreen mode

Usage:

asus-tuf-kbd-led on             # restore last state
asus-tuf-kbd-led off
asus-tuf-kbd-led white
asus-tuf-kbd-led red|green|blue
asus-tuf-kbd-led set ff8800     # custom color (hex)
asus-tuf-kbd-led intensity 128  # 0-255 brightness
asus-tuf-kbd-led info
Enter fullscreen mode Exit fullscreen mode

This works completely standalone and survives reboot, suspend, hibernate.

Option B: patched asusctl (upstream-quality fix)

For the proper solution that makes rog-control-center and asusctl themselves work, I patched the asusctl codebase to add I2C-HID LampArray support. The patch is open as PR to opengamingcollective/asusctl: https://github.com/OpenGamingCollective/asusctl/pull/147.

The patch:

  • adds an I2C-HID fallback path to aura_manager::init_hid_devices (existing USB path untouched)
  • adds DeviceHandle::maybe_lamparray() factory
  • adds Aura::is_lamparray flag and lamparray_write_effect/lamparray_set_brightness/lamparray_set_aura_power methods
  • routes the zbus methods through the LampArray branch when is_lamparray=true
  • fixes a HIDIOCGRAWINFO ioctl size bug (was 12, kernel struct is 8 bytes)
  • fixes HID_ID parsing (was string compare on padded hex, now numeric)
  • fixes a tokio::Mutex<AuraConfig> deadlock between zbus method and internal write
  • fixes a systemd Type=dbus timeout by registering the bus name before discovery
  • uses O_NONBLOCK on the hidraw open to avoid blocking inside the i2c-hid kernel driver

After the patched binary is installed, the test sequence is:

$ sudo install -m755 target/release/asusd /usr/bin/asusd
$ sudo systemctl restart asusd

$ busctl tree xyz.ljones.Asusd | grep aura
└─ /xyz/ljones/aura/lamparray_19b6

$ asusctl aura effect static -c ff0000   # keyboard turns red
$ asusctl aura effect static -c 00ff00   # green
$ asusctl aura effect static -c 0000ff   # blue
Enter fullscreen mode Exit fullscreen mode

And rog-control-center color picker on the keyboard tab now drives the hardware in real time.


Part 2 — Screen brightness in hybrid GPU mode

The diagnosis (corrected)

The internal panel is wired to the AMD iGPU, but in hybrid mode the kernel exposes three "backlight" devices, two of which are cosmetic:

Device Status on FA608WV
amdgpu_bl0 created but writes are no-ops (cosmetic)
nvidia_0 created by NVIDIA DRM but cosmetic in hybrid (NVIDIA isn't driving the panel)
nvidia_wmi_ec_backlight the real one — driver talks to the Embedded Controller via NVIDIA WMI

The trick is that nvidia_wmi_ec_backlight is the path that actually controls the panel even though it's named "nvidia" — in hybrid mode, the EC accepts the WMI command and adjusts the panel backlight regardless of which GPU is "active". The earlier dev.to deep dive concluded amdgpu_bl0 was the right one because the kernel registers it; in practice on this BIOS, it's cosmetic and only nvidia_wmi_ec_backlight works.

If you've already added acpi_backlight=native to your kernel cmdline or blacklisted nvidia_wmi_ec_backlight, undo both:

sudo rm -f /etc/modprobe.d/blacklist-nvidia-wmi-backlight.conf
sudo kernelstub -d 'acpi_backlight=native'    # if you previously added it
sudo update-initramfs -u
sudo reboot
Enter fullscreen mode Exit fullscreen mode

After reboot, only one backlight device should be present:

$ ls /sys/class/backlight/
nvidia_wmi_ec_backlight

$ brightnessctl --device=nvidia_wmi_ec_backlight set 30%   # screen actually dims
$ brightnessctl --device=nvidia_wmi_ec_backlight set 100%  # back to bright
Enter fullscreen mode Exit fullscreen mode

Hook into COSMIC Fn keys

If you're on Pop!_OS 24.04 with COSMIC desktop, override the system actions to use the real device:

# In ~/.config/cosmic/com.system76.CosmicSettings.Shortcuts/v1/system_actions
# Replace the busctl-based BrightnessUp/Down with brightnessctl on the working device
Enter fullscreen mode Exit fullscreen mode

Set them to:

BrightnessUp: "brightnessctl --device=nvidia_wmi_ec_backlight set +5%",
BrightnessDown: "brightnessctl --device=nvidia_wmi_ec_backlight set 5%-",
Enter fullscreen mode Exit fullscreen mode

After logout/login, the laptop's Fn brightness keys work nat.

Pop!_OS already grants the video group write access to the brightness file, so no extra udev rule is needed.


Part 3 — Hibernate (S4) when closing the lid

Why default suspend overheats

The FA608WV firmware only advertises s2idle (/sys/power/mem_sleep shows [s2idle]), not S3 deep sleep. Closing the lid puts the system into Modern Standby, where the OS may keep waking the CPU to service spurious events. Inside a backpack, the laptop runs hot.

S4 (hibernate-to-disk) on the other hand truly powers off the machine. The catch: Pop!_OS 24.04 doesn't enable hibernation by default — there's no resume swap, the kernel cmdline has no resume=, and the COSMIC desktop masks hibernate.target and sleep.target to avoid double-handling with the compositor.

Enable real hibernation

  1. Create a swapfile sized to your RAM (mine: 36 GB for 32 GB):
   sudo dd if=/dev/zero of=/swapfile bs=1M count=36864 status=progress
   sudo chmod 600 /swapfile
   sudo mkswap /swapfile
   sudo swapon /swapfile
   echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Enter fullscreen mode Exit fullscreen mode
  1. Find the resume offset of the swapfile:
   sudo filefrag -v /swapfile | awk '/^ *0:/ {gsub(/\.\./,"",$4); print $4; exit}'
Enter fullscreen mode Exit fullscreen mode
  1. Add kernel cmdline parameters via kernelstub (Pop!_OS uses systemd-boot):
   ROOT_UUID=$(findmnt -no UUID /)
   OFFSET=<from step 2>
   sudo kernelstub -a "resume=UUID=$ROOT_UUID"
   sudo kernelstub -a "resume_offset=$OFFSET"
Enter fullscreen mode Exit fullscreen mode
  1. Add resume configuration to initramfs:
   echo "RESUME=UUID=$ROOT_UUID resume_offset=$OFFSET" | sudo tee /etc/initramfs-tools/conf.d/resume
   sudo update-initramfs -u -k all
Enter fullscreen mode Exit fullscreen mode
  1. Unmask the systemd sleep targets (COSMIC blocks them by default):
   sudo systemctl unmask hibernate.target suspend.target hybrid-sleep.target suspend-then-hibernate.target sleep.target
Enter fullscreen mode Exit fullscreen mode
  1. Disable zram (otherwise systemd refuses to hibernate — zram is RAM, can't survive shutdown):
   sudo systemctl disable --now pop-default-settings-zram.service
   sudo apt remove pop-default-settings-zram
Enter fullscreen mode Exit fullscreen mode
  1. Wire the lid close to hibernate: In /etc/systemd/logind.conf:
   HandleLidSwitch=hibernate
   HandleLidSwitchExternalPower=hibernate
   HandleLidSwitchDocked=ignore
Enter fullscreen mode Exit fullscreen mode

Then sudo systemctl restart systemd-logind (or just reboot).

  1. Test:
   sudo systemctl hibernate
Enter fullscreen mode Exit fullscreen mode

The laptop should power off completely. Power back on and the session is restored, including all apps and browser tabs.

Verification

journalctl -b -1 | grep -iE 'hibern|S4' should show:

ACPI: PM: Preparing to enter system sleep state S4
PM: hibernation: Creating image
PM: hibernation: Need to copy N pages
ACPI: PM: Waking up from system sleep state S4
Enter fullscreen mode Exit fullscreen mode

And last -x will show a shutdown followed by a reboot covering the entire hibernated window.

The hibernation image takes a few seconds to write (depends on RAM in use), and resume is similar. Power consumption during hibernate is the same as a full shutdown.


What's not yet fixed (FA608WV)

  • per-key RGB: the LampArray on this SKU reports LampCount=1, so it's hardware-limited to single zone. No software fix.
  • asus-armoury power-limit attributes: the kernel asus_armoury driver tries to read attributes that this BIOS doesn't expose. Spammy errors in journal but functionally harmless. Upstream kernel cleanup.

References

If your laptop is a sibling SKU (FA608WI, FA608WD, etc.), check ls /sys/bus/hid/devices/ | grep 0B05 and sudo xxd /sys/bus/hid/devices/<id>/report_descriptor | grep "05 59". If both produce output, the same approach should work — possibly with a different PID. Report back, I'll update the asusctl PID whitelist.

Top comments (0)