Make Special HP Laptop Touchpad Toggle Key Work In Linux

I recently purchased a new (refurbished) HP “Elitebook” 8440p laptop (see previous blog post) and docking station, and installed my custom “remastered” copy of Antix Linux!  Before buying, I had tried out my remastered Antix live-DVD on my wife’s laptop (same model with lower resolution screen) and everything basic seemed to check out.  As I expected, this laptop has proved to be a great laptop for Linux but I’ve been somewhat surprised at the number of additional (and difficult) issues/hacks I’ve had to resolve/make to my (already very customized) remastered linux installation.  In my previous post I described the nightmare I went through getting Linux to boot with Windows 7, though this is not at all a fault of the computer or it’s hardware, but rather owing to the way Windows 7 was partitioned and installed.  Another hack / workaround I ended up having to make (after another extended, but fruitless search) was downgrading my “AccelMethod” from “SNA” back to “UXA” in my /etc/11/xorg.conf file to stop screen corruption and lockup when exiting X without having to add a “chvt 1; sleep 2” sequence to each logout/exit/reboot command script in the window-manager, and disabling the “Ctrl-Alt-Backspace” command.  Switching to “UXA” eliminated the need for these workarounds (after I tried everything else I could come up with).  This machine has a LATER version of Intel graphics (Mesa DRI Intel Ironlake Mobile x86/MMX/SSE2).  The tradeoff is a slight reduction in video performance from up to 2000 fps to around 1700 or so, only really noticeable by running glxgears (My Dell never broke above 900 fps, but WORKED flawlessly with SNA!).
The hardware was very similar (driver-wise) to that of my old Dell D620, but more modern (Intel CPU, Intel integrated graphics, sound, and network interfaces).  The first issue that needed tweaking was to activate all the custom special-function keys.  Nearly all laptops have them and they all seem to be different not only between brands, but also between models.  I had long ago gotten the Dell’s all working using a now rather old program called “hotkeys”, however, I had not used them in ages since my Dell has not had a working battery since forever, and I’ve been using it almost exclusively docked (and the lid closed).  I’ve found that hotkeys seems to not work well anymore due to trouble staying up between suspends, etc.  I began looking for a better solution and found “xbindkeys”. This program also includes a GUI which permits you to actually press each key and then add your desired action command and builds the config. file for you!  This strategy proved successful for all the special keys, including the little “led” light-up ones above the keyboard that look more like status indicators than press-able keys.  The only ones not configurable via xbindkeys were the “wifi switch” led/key and the “touchpad toggle” led/key.

~/.xbindkeysrc:

     #Quieter
     "/usr/bin/amixer -q set Master 4- unmute"
         m:0x0 + c:122
         XF86AudioLowerVolume

     #Louder
     "/usr/bin/amixer -q set Master 4+ unmute"
         m:0x0 + c:123
         XF86AudioRaiseVolume

     #MuteToggle
     "/usr/bin/amixer -q set Master toggle"
         m:0x0 + c:121
         XF86AudioMute

     #Display
     "sudo /usr/local/bin/switchdisplay.sh"
         m:0x0 + c:235
         XF86Display

     #Internet
     "/usr/local/bin/browse.pl"
         m:0x0 + c:180
         XF86HomePage

     #LockScreen
     "/usr/local/bin/screenoff"
         m:0x0 + c:160
         XF86ScreenSaver

     #Dimmer
     "/usr/local/bin/setbacklight.sh -60"
         m:0x0 + c:232
         XF86MonBrightnessDown

     #Brighter
     "/usr/local/bin/setbacklight.sh 60"
         m:0x0 + c:233
         XF86MonBrightnessUp

     #ScreenToggle
     "/usr/local/bin/togglebacklight.sh"
         m:0x0 + c:248
         NoSymbol

     #Suspend
     "sudo /usr/local/bin/s2ram"
         m:0x0 + c:150
         XF86Sleep

The wifi key “works” in that it uses rfkill to toggle the wifi.  For most people, this will probably work fine out of the box, but I configure my networks manually, and need for the wifi to go off (and ethernet to come on) when docking so that my home network comes up on the ethernet connection (I needed to be able to execute a custom script whenever this key was pressed).  I ended up getting this key working by adding an ACPI event and script (see below).
/etc/acpi/events/hp8440p-wireless:

     event=PNP0C14:01 00000080 00000000
     action=/etc/acpi/hp-wifi-jwt.sh

In Linux Live-CD:

/etc/acpi/hp-wifi-jwt.sh

     event=PNP0C14:01 00000080 00000000
     #!/bin/sh

     test -f /usr/share/acpi-support/key-constants || exit 0

     . /usr/share/acpi-support/key-constants

     wifiblocked=`rfkill list wifi|grep "yes"`
     if [ "X$wifiblocked" = "X" ]; then
         echo "..WIFI BUTTON PRESSED: wifi not blocked!" >>/tmp/acpi.dbg
         #DO WHATEVER TO RESTORE PREV. CONNECTION:
     else
         echo "..WIFI BUTTON PRESSED: wifi BLOCKED!" >>/tmp/acpi.dbg
         #DO WHATEVER WE WANT WHEN WIFI TURNED OFF:
     fi

HP Touchpad Toggle Led/KeyI got all the special HP light-up buttons above the keyboard working with xbindkeys EXCEPT the one that looks like a tiny touchpad, that’s supposed to toggle the touchpad.  That little bugger just did not want to work, not even in Windows-7?!  The “touchpad toggle” led/key lights up cyan on startup and the touchpad is enabled.  Pressing it turns it orange and (should) disable the touchpad. Touching again turns it back to cyan (should re-enable touchpad).  In reality, it does nothing but change color when pressed.  X does not see it except the very first time it’s pressed, in which case xev sees a continuous spewing of keypresses and releases of keycode 138 killable by pressing Ctrl-C.  dmesg reveals that the kernel sees the alternate press as unassigned scancode “e058”.  Subsequent presses produce nothing visible to X until you reboot.  I tried adding the setkeycodes command the kernel suggested, googled and googled and could not find anything that would make this key visible to X. The console program showkey would show it alternately returning keycode 138 and keycode 140 (the one I assigned with setkeycodes) as I wanted, but for some reason nothing in X could see anything when the key was pressed.  Obviously, the key was seen by the kernel.  Several google pages described how to “translate” these kernel keycodes to X, including xmodmap, but nothing worked.
It was not like this particular key was very important to me, but at this point it had just become a determined challenge to force it to work!  I finally stumbled upon a tiny C program that could monitor the keyboard input device and report each key pressed.  I ended up modifying this program to look for these two keycodes and calling a command whenever one was seen.  I then set this program up as a tiny daemon so that I can now press the key and toggle the state of the touchpad!  Why?  Because I could and because I had already wasted too much time trying to figure this out!  😀
I finally found, stole and hacked up a tiny daemon C program that DOES make it work!  You need to do the following:
1)  in rc.local or somewhere in startup scripts add: /usr/bin/setkeycodes e058 140
2)  compile this little C program “keycheckd” (gcc -o keycheckd keycheckd.c) and place the compiled binary in /usr/local/bin/, and chown 4755 keycheckd
3)  In your X desktop session’s startup script do (must be started from w/n X desktop, NOT startup or console!):

     xset -r 138 -r 140 -r 146 -r 148
     keycheckPid=`pidof keycheckd`
     [[ "X$keycheckPid" == "X" ]] && /usr/bin/nice -n 10 /usr/local/bin/keycheckd &

The source (keycheckd.c) (note: you could add other keysyms / actions, if needed):

#include <stdlib.h>>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>

/*
JWT:THIS DAEMON PGM WATCHES THE KEYBOARD FOR PRESSES OF THE SPECIAL HP ELITEBOOK 8440P "TOGGLE-KEYPAD" KEY WHICH X (xev, xbindkey & FRIENDS REFUSE TO SEE AFTER THE 1ST PRESS PER REBOOT) AND INVOKES THE COMMAND TO TOGGLE THE TOUCHPAD (AS IT WAS DESIGNED TO DO). X DOESNPT LIKE THIS SPECIAL KEY B/C IT ONLY SENDS A "KeyPressed" EVENT W/NO CORRESPONDING "KeyReleased" EVENT. IT WORKS LIKE OTHER "LED"DED KEYS SUCH AS CAPSLOCK AND NUMLOCK! THE INITIAL STATE IS "BLUE" (TOUCHPAD ON), FIRST PRESS SENDS KEYCODE 140 (148 TO xbindkeys) AND SWITCHES THE LED TO "ORANGE". NEXT PRESS SENDS KEYCODE 138 (146) AND SWITCHES THE LED BACK TO "BLUE". SUBSEQUENT KEYPRESSES ALTERNATE BETWEEN THESE TWO STATES. I STOLE THIS PGM. FROM HERE: http://stackoverflow.com/questions/20943322/accessing-keys-from-linux-input-device.  I MODIFIED IT TO ONLY CARE A/B THESE TWO KEYCODES. NOTE: I HAD TO ADD "setkeycodes e058 140" TO rc_local.pl WHEREAS THE OTHER ONE (138) WAS ALREADY RECOGNIZED BY THE KERNEL. THIS PROGRAM SHOULD BE STARTED THE FIRST TIME YOUR WINDOWMANAGER STARTS UP (.afterstep/autoexec in my case)
*/

int main(void) {
    const char *dev = "/dev/input/event0"; /* or whatever your keyboard input is */
    struct input_event ev;
    ssize_t n;
    int fd;

    fd = open(dev, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
        return EXIT_FAILURE;
    }
    while (1) {
        n = read(fd, &ev, sizeof ev);
        if (n == (ssize_t)-1) {
            if (errno == EINTR)
             continue;
            else
             break;
        } else
        if (n != sizeof ev) {
            errno = EIO;
            break;
        }
        if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2) {
            /* printf("0x%04x (%d)\n", (int)ev.code, (int)ev.code); */
            if ((int)ev.code == 138) {
                /* TURN ON TOUCHPAD: */
                /* printf("--138: turn touchpad ON!\n"); */
                system ("/usr/bin/synclient TouchpadOff=0");
            } else if ((int)ev.code == 140) {
                /* TURN OFF TOUCHPAD: */
                /* printf("--140: turn touchpad off!\n"); */
                system ("/usr/bin/synclient TouchpadOff=1");
            }
        }
    }
    fflush(stdout);
    fprintf(stderr, "%s.\n", strerror(errno));
    return 0;
}

Now, when you press this “key”, it should turn orange and actually disable your touchpad, a second press should turn it back blue and re-enable your touchpad.  I believe you could add the “/usr/bin/synclient TouchpadOff=1” in your X session startup script above where you start this daemon, and reverse the two “TouchpadOff=#” commands in it to have it start out disabled, then toggle to enabled, then disabled, etc.
Advertisements

Feel Free to Comment (Name/Email/Website optional):

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: