Architecture

Revision 10 as of 2008-12-24 00:33:39

Clear message


NOTE: This document is a work in progress. ACPI is a complex subsystem and impossible for any one person to fully understand. Please take a few minutes to improve this document with the bits you know!


Current architecture

hotkeys.png

Target architecture

hotkeys-sb.png

Here is the birds-eye view of how key mapping works: The kernel recognizes the key and establishes a raw scancode and maps it to an arbitrary kernel keycode (e.g. e06d -> 120). Xorg reads the kernel's keycode table, and generates it's own list of X keycodes (e.g. 120 -> 136). X then maps each X keycode to a keysym (e.g. 136 -> XF86AudioMedia). Finally, the window manager maps the keysym to a desired window manager shortcut action or an application command (e.g. XF86AudioMedia -> rhythmbox).

Part A: Kernel keyboard mapping

Find the "raw scancode" of the key in question. Typically for PS/2 keyboards this can be done from a tty console by hitting the key, and looking at dmesg output. For instance, if we are hitting the 'Media' key on a Dell Inspiron, we might see:

        atkbd.c: Unknown key pressed (translated set 2, code ... on isa0060/serio0).
        atkbd.c: Use 'setkeycodes e06d <keycode>' to make it known.

In the above, e06d is what we're looking for.

Another way to get the kernel keycodes is to shutdown X and hotkey-setup (e.g. /etc/init.d/gdm stop ; /etc/init.d/hotkey-setup stop) and then run showkey -s for raw scancodes or showkey -k for keycodes. Then restart hotkey-setup and gdm. This will give output for Media on a Dell Inspiron 1505N like:

0x9c
0x00 0x80 0xe2
0x80 0x81 0xe2

For USB controlled keys, an alternate approach is to run lshal -m from a console window and then hit the key. This should return something like:

    17:51:47.996: platform_i8042_i8042_KBD_port_logicaldev_input condition ButtonPressed = media

In this case, media is the key we're interested in. If ButtonPressed shows up as undefined, then the USB driver needs to be updated to support mapping this key to a Linux keycode (currently these are all compiled into the USB driver).

In some cases, your keys may be generating ACPI events instead of standard keycodes. To check this, run sudo tail -f /var/log/acpid and then hit the key. You should see something like:

received event "ibm/hotkey HKEY 00000080 00001010"

The interesting bits here is the part between the quotes. [What do we do with this info?]

On IBM/Lenovo laptops, some keys are only accessible via /dev/nvram.

Identify the kernel keycode to use by referring to /usr/share/hotkey-setup/key-constants. For example, browsing through the file we find KEY_MEDIA:

...
KEY_BRIGHTNESSDOWN=224
KEY_BRIGHTNESSUP=225
KEY_MEDIA=226
KEY_SWITCHVIDEOMODE=227
KEY_KBDILLUMTOGGLE=228
...

These codes are defined in /usr/include/linux/input.h.

Part B: Hal

Set up the kernel keycode mapping for the keyboard layout. These can be set up via HAL quirks. See Keymap Quirk Debugger for details. Essentially, you need to add or update a section in the appropriate FDI file in /usr/share/hal/fdi/information/10freedesktop/, such as 30-keymap-dell.fdi for a Dell Inspiron 1505N:

...
        <!-- Dell Inspiron 9400, 640m, E1505/6400 -->
        <match key="/org/freedesktop/Hal/devices/computer:system.hardware.product" prefix_outof="MP061;MXC061;MM061"> 
          <append key="input.keymap.data" type="strlist">e005:brightnessdown</append> <!-- Fn+Down arrow Brightness Down -->
          <append key="input.keymap.data" type="strlist">e006:brightnessup</append> <!-- Fn+Up arrow Brightness Up -->
          <append key="input.keymap.data" type="strlist">e007:battery</append> <!-- Fn+F3 battery icon -->
          <append key="input.keymap.data" type="strlist">e008:wlan</append> <!-- Fn+F2 Toggle WiFi -->
          <append key="input.keymap.data" type="strlist">e008:bluetooth</append> <!-- FIXME Bluetooth switch (same as wireless) -->
          <append key="input.keymap.data" type="strlist">e009:ejectclosecd</append> <!-- Fn+F10 Eject CD -->
          <append key="input.keymap.data" type="strlist">e00a:suspend</append> <!-- Fn+F1 hibernate -->
          <append key="input.keymap.data" type="strlist">e00b:switchvideomode</append> <!-- Fn+F8 CRT/LCD -->
          <append key="input.keymap.data" type="strlist">e012:media</append> <!-- MediaDirect button (house icon) -->
          <append key="info.capabilities" type="strlist">input.keymap</append>
        </match>
...

So for instance, to support the e06d code we found before, we'd create a new section like the above, substituting in e06d for e012 (and any other codes that need remapping), into a new product section.

Use lshal | grep system.hardware.product and lshal | grep system.hardware.vendor to determine the product name and vendor of your laptop.

To see if hal has picked up your keymaps, restart the hal daemon and use lshal | grep keymap, which gives output like:

  info.callouts.add = {'hal-setup-keymap'} (string list)
  info.capabilities = {'input', 'input.keyboard', 'input.keypad', 'input.keys', 'input.keymap', 'button'} (string list)
  input.keymap.data = {'e005:brightnessdown', 'e006:brightnessup', 'e007:battery', 'e008:wlan', 'e008:bluetooth', 'e009:ejectclosecd', 'e00a:suspend', 'e00b:switchvideomode', 'e012:media'} (string list)

Part C: Gnome/KDE hotkey mapping

At this point, gnome-keybinding-properties should now be able to detect your key. Check this by setting it for "Launch help browser" for example.