Architecture (Ubuntu 9.10)
Here is the bird's-eye view of how key mapping works: The kernel recognizes the key and establishes a raw scancode and maps it to a kernel keycode (e.g. e06d -> 120). Xorg translates the kernel keycode (which is Linux specific) to its own list of X keysyms (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 -> launch rhythmbox).
Kernel keyboard mapping
All keyboard keypresses and ACPI events associated with keys are intended to be translated to key codes in the kernel. Thus scan codes are highly hardware specific, while key codes abstract away from that and put a well-known semantics ("next song" or "letter A") to a constant. These key codes are defined in /usr/include/linux/input.h.
MatthewGarrett is actively pursuing this in kernel upstream:
If a key's scancode is unknown to the kernel's keyboard mapping layer, this will typically result in a kernel log message when pressed. For instance, hitting the 'Media' key on a Dell Inspiron, we might see in dmesg output:
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.
Often, the keys will already be mapped but may be mapped to an incorrect keycode. In this case, use showkey -s from the console to see the raw scancodes, or showkey -k to see the keycodes. This will give output for Media on a Dell Inspiron 1505N like:
0x9c 0x00 0x80 0xe2 0x80 0x81 0xe2
Once the correct keymapping has been determined, this is configured either by setting a default in the per-platform hotkey kernel driver (in the Linux kernel itself), or by overriding the kernel keymap using udev's keymap extension (see below).
You should not add any keymappings to the obsolete hotkey-setup or hal-info packages.
udev key maps
If the steps above revealed that the kernel does not know the key symbol for a particular key (or assigns the wrong symbol), the key can be added/changed in udev. This has two parts:
/lib/udev/rules.d/95-keymap.rules maps laptop vendor/product names to map file names.
map files in /lib/udev/keymaps/ with the scan code → key code table
Please see /usr/share/doc/udev/README.keymap.txt for details about how to check for and fix wrong keys, create those maps, and submit them upstream.
In some cases, your keys may be generating ACPI events instead of standard keycodes. To check this, stop acpid (if necessary), run cat /proc/acpi/event 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.
If a key generates an ACPI event but not a keypress, then the kernel needs to implement a platform-specific kernel ACPI driver that translates these ACPI events to input events on a dedicated input device (e.g., thinkpad_acpi).
Note that an "input event" is not restricted to a "key event". Other types of events are e. g. "power", "sound" (e. g. jack sense), or "movement" (mouse/joystick).
In the past, some these events were handled in hald-addon-acpi and acpi-support, but these are deprecated. All ACPI events should become proper input events in the kernel nowadays.
On IBM/Lenovo laptops, some keys are only accessible via /dev/nvram. This should be superseded by the thinkpad_acpi kernel module, but it's unconfirmed if this is the case for all older ThinkPad models. (It is definitely superseded for the T60 model.)
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.
Architecture in Ubuntu 8.10 and earlier
Architecture in Ubuntu 9.04 with hal