special buttons

There are three special buttons, the eject and fn keys and the power button.

eject

Attention!

Update: Version 0.6.8 of pbbuttonsd has a patch (different from mine below) that makes the eject button work out of the box.

This button seems to be connected via its own USB device which is a HID device on the Consumer Page (check out the HID Usage Tables pages 75 and 77). I checked out pbbuttonsd and made the following patch for it which makes the eject button work properly on my system.

--- orig/src/input_manager.c    2004-09-24 17:25:51.000000000 +0200
+++ mod/src/input_manager.c     2005-03-23 21:03:38.346817376 +0100
@@ -204,7 +204,7 @@
                sprintf(filename, "/dev/input/event%d", n);
                if ((fd = PrivoxyWindowOpen(filename, O_RDONLY)) >= 0) {
                        ioctl(fd, EVIOCGBIT(0, EV_MAX), bit);
-                       if (test_bit(EV_KEY, bit) && test_bit(EV_REP, bit)) {
+                       if ((test_bit(EV_KEY, bit) && test_bit(EV_REP, bit)) || (bit[0] == 0x13)) {
                                ioctl(fd, EVIOCGID, id);
                                if (id[ID_PRODUCT] != base->evdevs[m].product ||
                                        id[ID_VENDOR]  != base->evdevs[m].vendor) {

I'm sure this can be handled better, but I think there must be a point to the fact that pbbuttonsd checks for the EV_REP bit. Devices on the consumer page don't set the EV_REP bit (see drivers/usb/input/hid-input.c around line 230 for the code). Maybe that is wrong, but I doubt it, after all a device like this has buttons like play, pause, record, eject and more which are all one-shot actions and EV_REP (event repeat) doesn't make sense. So it actually makes a lot of sense that Apple decided to make the eject button a consumer device, just the fact that pbbuttonsd ignores anything that cannot repeat is weird. Therefore I think the line I modified above would better be rewritten to just

if (test_bit(EV_KEY, bit)) {

Well, I'll leave that to the author of pbbuttonsd to decide.

fn

The fn button generates an event in the corresponding event device, but showkey doesn't pick it up. Initially I assumed there's a problem in the kernel driver, but it turns out that for some reason the fn key generates an event of type EV_ABS instead of EV_KEY. Therefore, the following patch adds a quirk to the ever-growing list.

Attention!

Matthew Denner just told me that his UK model PowerBook actually has a device ID of 0x020F so I updated the patch to reflect that.

diff -ur linux-2.6.11.5.orig/drivers/usb/input/hid-core.c linux-2.6.11.5.mod/drivers/usb/input/hid-core.c
--- linux-2.6.11.5.orig/drivers/usb/input/hid-core.c    2005-03-19 07:34:58.000000000 +0100
+++ linux-2.6.11.5.mod/drivers/usb/input/hid-core.c     2005-03-24 01:45:05.717546800 +0100
@@ -1494,6 +1494,10 @@
 #define USB_VENDOR_ID_DELORME          0x1163
 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100

+#define USB_VENDOR_ID_APPLE            0x05AC
+#define USB_DEVICE_ID_POWERBOOK_KB_US  0x020E
+#define USB_DEVICE_ID_POWERBOOK_KB_UK  0x020F
+
 static struct hid_blacklist {
        __u16 idVendor;
        __u16 idProduct;
@@ -1585,6 +1588,9 @@
        { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },

        { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
+
+       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_POWERBOOK_KB_US, HID_QUIRK_POWERBOOK_FN_BUTTON },
+       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_POWERBOOK_KB_UK, HID_QUIRK_POWERBOOK_FN_BUTTON },

        { 0, 0 }
 };
diff -ur linux-2.6.11.5.orig/drivers/usb/input/hid.h linux-2.6.11.5.mod/drivers/usb/input/hid.h
--- linux-2.6.11.5.orig/drivers/usb/input/hid.h 2005-03-19 07:34:49.000000000 +0100
+++ linux-2.6.11.5.mod/drivers/usb/input/hid.h  2005-03-24 01:42:51.537945200 +0100
@@ -242,6 +242,7 @@
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_7          0x080
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5          0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON         0x200
+#define HID_QUIRK_POWERBOOK_FN_BUTTON          0x400

 /*
  * This is the global environment of the parser. This information is
diff -ur linux-2.6.11.5.orig/drivers/usb/input/hid-input.c linux-2.6.11.5.mod/drivers/usb/input/hid-input.c
--- linux-2.6.11.5.orig/drivers/usb/input/hid-input.c   2005-03-19 07:34:55.000000000 +0100
+++ linux-2.6.11.5.mod/drivers/usb/input/hid-input.c    2005-03-24 02:14:40.054806536 +0100
@@ -409,6 +409,11 @@
        if (!usage->type)
                return;

+       if ((hid->quirks & HID_QUIRK_POWERBOOK_FN_BUTTON) && (usage->type == EV_ABS) && (usage->code == 40)) {
+               input_event(input, EV_KEY, 0x61 /* seems to be Control_R in X which this KB doesn't have */, value);
+               return;
+       }
+
        if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
                || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
                if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;

I know the key press actually arrives in userspace via the input/eventX device, but is not really usable that way. pbbuttonsd could of course use it, but I think that one might want to use it in different places as well (X for example).

power button

The power button on this PowerBook is not connected to the USB keyboard, but seems to send PMU events instead. If I run something like

cat /dev/pmu | (while true ; do hd -n6 | grep 00000000 ; done )

I get

00000000 40 10 00 30 00 00

every second or so (or something like that) and

00000000 40 18 00 30 00 00

while the power button is pushed. (Actually, I'm not quite sure about those 6 bytes except for the first 2 as I'm writing this at uni with my powerbook at home...)

I have a patch for pbbuttonsd that allows one to add a statement like

  SleepKey = pmupowerbutton

to the pbbuttonsd.conf.