Make USB keyboard stay awake and disable laptop internal keyboard
I’m on Arch linux, on a laptop, and I just got a nice Poker II keyboard, which is a monumental joy to type on. When I first plugged in the keyboard, everthing worked great, but then I quickly ran into an issue with it falling asleep after two seconds. This would be fine, except it took a whole keypress to wake it up, and that keypress didn’t register with the OS. I’d type a word, and it would inevitably end up missing its first letter. Annoying. I quickly tracked it down to the keyboard falling asleep due to laptop USB power saving. Now to google. Here’s what I found to fix the issue:
Find the vendor and product ids for your device
The keyboard luckily output some messages I could easily see with dmesg
:
[ 2327.000711] input: Heng Yu Technology Poker II as /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.1/input/input29
[ 2327.000899] hid-generic 0003:0F39:0611.000D: input,hidraw2: USB HID v1.10 Device [Heng Yu Technology Poker II] on usb-0000:00:14.0-2/input1
In the second line, the id is 0F39:0611
. That is the idVendor
and idProduct
separated by a colon, respectively.
Change udev rules to keep the power on
I added the following line to /etc/udev/rules.d/91-local.rules
(create the file if it doesn’t exist):
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0f39", ATTR{idProduct}=="0611", TEST=="power/control", ATTR{power/control}="on"
Change laptop-mode config
If you have laptop-mode-tools
installed, it’ll interfere with the above udev rule. Modify /etc/laptop-mode/conf.d/usb-autosuspend.conf
to blacklist your device, change the AUTOSUSPEND_USBID_BLACKLIST
value as you can see in the file below (about halfway through the file).
# The list of USB IDs that should not use autosuspend. Use lsusb to find out the
# IDs of your USB devices.
# Example: AUTOSUSPEND_USBID_BLACKLIST="046d:c025 0123:abcd"
AUTOSUSPEND_USBID_BLACKLIST="0f39:0611"
Disable the internal, default keyboard
I wanted to put the poker on top of my laptop and use it in the same position as the laptop’s internal keyboard, but it kinda crushes the keys (the Poker II is not super lightweight). I had to disable the internal keyboard. To do that, first I needed to find the keyboard’s id using xinput
:
ƛ xinput
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ SynPS/2 Synaptics TouchPad id=11 [slave pointer (2)]
⎜ ↳ ELAN Touchscreen id=13 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Power Button id=8 [slave keyboard (3)]
↳ Sleep Button id=9 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=10 [slave keyboard (3)]
↳ Acer WMI hotkeys id=12 [slave keyboard (3)]
↳ HD WebCam id=15 [slave keyboard (3)]
↳ Heng Yu Technology Poker II id=14 [slave keyboard (3)]
↳ Heng Yu Technology Poker II id=16 [slave keyboard (3)]
There you can see my Poker II shows up twice for some reason, and my laptop’s normal keyboard is labeled “AT Translated Set 2 keyboard”. I discovered the hard way that those ids can change, but luckily the xinput
command takes names as well, so we’ll use that string to make a bash script that toggles the keyboard on/off:
#!/bin/bash
#/home/me/bin/toggle-keyboard
kb_name='AT Translated Set 2 keyboard'
if xinput list "$kb_name" | grep -i --quiet disable; then
xinput enable "$kb_name"
else
xinput disable "$kb_name"
fi
Now everything’s peachy!.. Almost. One further level of convenience would be to enable/disable the internal keyboard whenever I un/plug my external one. udev
to the rescue again! udev
can run a script when those “add” and “remove” events occur. I modified the udev
rule to the following:
ACTION=="add", ENV{DISPLAY}=":0.0", ENV{XAUTHORITY}=="/home/me/.Xauthority", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="0f39", ENV{ID_MODEL_ID}=="0611", RUN+="/home/me/bin/disable-keyboard", TEST=="power/control", ATTR{power/control}="on"
ACTION=="remove", ENV{DISPLAY}=":0.0", ENV{XAUTHORITY}=="/home/me/.Xauthority", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="0f39", ENV{ID_MODEL_ID}=="0611", RUN+="/home/me/bin/enable-keyboard"
Notice a couple of things: RUN
and ENV{WHATEVER}
. I switched the ATTR
parts to ENV
because apparently ATTR
isn’t always available, but ENV
is. If you want to see what ENV
variables are available, run udevadm monitor --property
and then (un)plug your device. The RUN
part is pretty self-explanatory, and here are the scripts:
#!/bin/bash
xinput enable 'AT Translated Set 2 keyboard'
And to disable:
#!/bin/bash
xinput disable 'AT Translated Set 2 keyboard'
The trickiest bit was figuring out why this didn’t work at first, but it turns out that even though udev
will run a script just fine, in order to get xinput
to work, I had to add the ENV{DISPLAY}=":0.0"
and ENV{XAUTHORITY}=="/home/me/.Xauthority"
parts to the udev rule. Took me a while to figure that one out.
Now everything is fully automatic, I don’t really need my toggle script. I could combine both of those xinput scripts into a single toggle script, or a script that takes an argument, but I liked the uber-explicitness of having two separate scripts. I may change my mind about that later.
I hope this is helpful to others! Happy keyboarding!
EDIT:
I made it better:
ACTION=="add", ENV{XAUTHORITY}="/home/me/.Xauthority", ENV{NAME}=="*?", ENV{DISPLAY}=":0.0", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/home/me/bin/disable-keyboard", TEST=="power/control", ATTR{power/control}="on"
ACTION=="remove", ENV{XAUTHORITY}="/home/me/.Xauthority", ENV{NAME}=="*?", ENV{DISPLAY}=":0.0", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/home/me/bin/enable-keyboard"
This should detect any keyboard added to the system. I arrived at this by examining the output from the udevadm monitor --property
command more closely, and choosing an appropriate UDEV
event for this rule to apply to. I found out that keyboards get both a ID_INPUT_KEYBOARD
and a NAME
property, and it seems to work pretty well, while being totally generic to all keyboards! Just don’t forget to add your keyboard to the AUTOSUSPEND_USBID_BLACKLIST
. ; )