auf.kante
Gunnar macht sich selbststaendig und fliegt auf
die Schnauze
wird erfolgreich. Wer mag darf zuschauen.
Gunnar is starting his business. He will certainly
fail succeed. You may watch.
auf.kante
Website sections
Home
Linux and the keyboard
Last changed: 2007-04-10 [11:46]
Content:

Some notes on the keyboard in a Linux system
 
Back to Personal Wiki
Table of contents
; INVALID LISP CODE
Microsoft Natural Multimedia Keyboard
Inside the keyboard
Inside the machine - The i8042
A key stroke arrives
The linux input system
TODO
Links

Microsoft Natural Multimedia Keyboard

I bought a Microsoft Natural Multimedia Keyboard because of its splitted keyboard which I consider to be really comfortable. But since I do not use it under its "natural" operating system all those fancy keys that are present on the keyboard do pretty much nothing. In addition to that I also wanted to learn a little bit more about keyboard mapping when I am working in a remote terminal. Time to gain some more insight on the relationship between the Linux kernel and keyboards.

Inside the keyboard

The initial steps of transmitting the information of a key event happen inside the keyboard. A small chip integrated into the keyboard constantly checks for activated keys. Once this chip identifies that the user pressed a key it encodes the type of key by sending one or several bytes over a serial line (the keyboard cable) to the computer. The chip on the keyboard can already choose between three different types of encodings for the keyboard events. They are identified as scancodes 1 - 3.

Depending on the scancode each key stroke is reported in a specific way. Usually each key stroke will elicit a make code when it is being pressend and a break code once it is released.

The following description of the three scancode sets was taken from Keyboard scancodes:

For Set 1, if the make code of a key is c, the break code will be c+0x80. If the make code is e0 c, the break code will be e0 c+0x80. The Pause key has make code e1 1d 45 e1 9d c5 and does not generate a break code.

For Set 2, if the make code of a key is c, the break code will be f0 c. If the make code is e0 c, the break code will be e0 f0 c. The Pause key has the 8-byte make code e1 14 77 e1 f0 14 f0 77.

For Set 3, by default most keys do not generate a break code.

Usually mode 2 or 3 will be used. There is no way to influence this scan code mode directly from the keyboard. The only way to communicate with the keyboard is the keyboard controller chip inside the computer. So this will be the next element to look at.

Inside the machine - The i8042

The "i8042" chip (or any of its descendants) is the keyboard controller inside the computer and attached to the PS/2 port of newer computers as well as to the system bus (this excludes USB keyboards). This keyboard controller is handled by the drivers/input/serio/i8042.c code in the linux kernel. The input and output ports are defined in drivers/input/serio/i8042-io.h.

#define I8042_COMMAND_REG	0x64
#define I8042_STATUS_REG	0x64
#define I8042_DATA_REG		0x60

You may use the command register 0x64 to write commands to the i8042. The following line should initiate a self test on the keyboard:

outb 0x64 0xaa && inb 0x60

Usually the output should be 0x55 indicating "no failure" (for more commands see http://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html).

The i8042 chip is mapped to the "/sys" filesystem under linux and you should be able to find some information about its current state there. My keyboard controller is listed under "/sys/bus/serio/devices/serio0". The "descripton" file inside one of the serio directories should identify it as "i8042 Kbd Port". There is a set file in the same directory that will display the current keyboard set that is being used (in my case this is set 2 for the microsoft keyboard).

You can also try reading the current setting directly from the keyboard using something like the following sequence:

outb 0x60 0xf0 && inb 0x64 && inb 0x60 && sleep 1 && outb 0x60 0x00 && inb 0x64 && inb 0x60

Writing 0xf0 followed by 0 queries the mode, resulting in a scancode byte 43, 41 or 3f from the keyboard (so in case this returns 41 for scancode 2). This holds true as long as the i8042 is in translated mode (see below). Else you will receive the expected 1, 2 or 3. More keyboard commands can be found here: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html.

Using the sys filesystem you can easily activate the debugging mode of the i8042 kernel module using:

echo "Y" > /sys/module/i8042/parameters/debug

This does generate a lot of output in your logfile (one or several lines per keystroke).

A key stroke arrives

So far we know that the keyboard can return a keystroke in one of three different "languages" (scancodes). But this is not the only mangling a key stroke may be submitted to. The i8042 is usually set to translate key codes from scancode 2 to scancode 1 (it does that by turning an f0 prefix into an OR with 80 for the next byte). You will find a comprehensive translation table at http://www.win.tue.nl/~aeb/linux/kbd/scancodes-9.html#scancodesets

You can test your i8042 for translation mode using the following input:

outb 0x64 0x20 && inb 0x60

I get a 0x47 here and the active bit 7 indicates translated mode (see also http://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html).

Actually the situation described here - translated scancode 2 - seems to be the standard situation in most cases. Whatever the situation of your keyboard and its controller, the intention of the atkbd.c (in drivers/input/keyboard/atkdb.c) file is to map all weird scancodes to a standard key code that has a defined meaning for the kernel. So if you press the key in the upper left corner on your keyboard (usually labelled "Esc"), the kernel should always produce key code 1, whatever scancodes and translation are used by the keyboard.

This key code is then transferred to the kernel input system.

What about my microsoft keyboard? It has a few special keys that are probably not linked to a specific key code inside the Linux kernel since they are uncommon among keyboards. But there exists the possibility to redefine association between scancodes and key codes using the tool setkeycodes.

One can play around with the standard keys:

setkeycodes 1e 31

Pressing the key "a" (scnacode 1e) now results in an "s". This might not happen in your case since there are additional layers involved until the letter actually gets displayed. So this might depend on the configuration of your machine. Revert this by linking the scancode 1e to keycode 30 again.

setkeycodes 1e 30

The main use of this tool is to make previously unknown keys known to the linux kernel. The Microsoft keyboard emits the scancode e03b for the "Help" key on the keyboard and using

setkeycodes e03b 204

will now emit the keycode 204 (which is not linked to any key previously) for the "Help" key.

The microsoft natural keyboard can be tuned to emit a key code for every single keypress by using the following "setkeycode" command:

setkeycodes e016 174 e064 212 e03c 213 \
            e005 216 e03b 138 e008 131 \
            e007 129 e03e 144 e03f 134 \
            e040 206 e041 148 e042 149 \
            e043 145 e023 202 e057 203 \
            e058 210

# 174: KEY_EXIT     (Abmelden)
# 212: KEY_CAMERA   (Eigene Bilder)
# 213: KEY_SOUND    (Eigene Musik)
# 216: KEY_CHAT     (Messenger)
# 138: KEY_HELP     (Hilfe)
# 131: KEY_UNDO     (Rückg)
# 129: KEY_AGAIN    (Wiederherstell)
# 144: KEY_FILE     (Neu)
# 134: KEY_OPEN     (Öffnen)
# 206: KEY_CLOSE    (Schliessen)
# 148: KEY_PROG1    (Antwort)
# 149: KEY_PROG2    (Weiterleiten)
# 145: KEY_SENDFILE (Senden)
# 202: KEY_PROG3    (Rechtschreib)
# 203: KEY_PROG4    (Speichern)
# 210: KEY_PRINT    (Drucken)

The listing of key codes below the command relates to the naming in the include/linux/input.h file. I tried to map the keys to the corresponding meaning that I found there. Hopefully that makes it easier to configure the subsequent systems.

The linux input system

The input event system handles keyboard events with the driver drivers/char/keyboard.c. This module is responsible for relaying the setkeycode call to the actual device driver. In addition it handles LED switching events, keyboard beeps and setting the keyboard rate. This driver actually merges all keyboards to one in case you have several connected to your system.

But the most important part is to map each key code to some useful output characters. While the whole translation part happening in the drivers described above was necessary to map each key to a well defined key code the approach disregarded a very important fact: not every keyboard layout is the same and in fact the ordering (or labeling) of keys are adapted to the language of the users.

So each key code now needs to be translated into the correct language. At this point we need a key map that describes the labels on the specific keyboard in use.

It would be nice if this would be the only problem at this point but in fact there are different requirements depending on the program receiving the key strokes. There exist two major modes which handle the key input in different ways. One is the kernel console (text mode) and the other one is the X server (graphical mode).

Mapping keystrokes on the console

Since the console is tightly integrated into the kernel, all preprocessing to the correct characters is handled by the drivers/char/keyboard.c module. In order to have the keyboard driver emit correct character codes you need to set the driver to unicode mode, which can be done using the kbd_mode command (Don't do this while you are in X!):

kbd_mode -u

There exists an older mode that uses ASCII characters but it is deprecated and should not be used anymore. Having your operating system to use unicode at its core level actually helps to prevent a lot of trouble with character conversions at later times. Don't expect every program and tool to be unicode aware (so there might be some problems) but it is clear that the computer world will develop more and more into that direction.

Key maps

In addition to the unicode mode selection we need the keymap that maps the kernel key codes to actual characters (or character sequences). Since we selected unicode mode this map has to be a unicode map but more on this later.

It is possible to set an initial keymap for the kernel when you compile it but it is better to set the keymap during runtime. The command loadkeys is responsible for modifying the kernel keymap. You need to specify a keymap file that will be parsed and written to kernel memory.

How can we define a keymap? The following is a short extract from a file that can be fed into loadkeys:

#             Normal           Shift            AltGr           Strg
keycode   1 = Escape           Escape
keycode   2 = one              exclam
keycode   3 = two              quotedbl         two             nul
keycode   4 = three            numbersign       three           Escape
keycode   5 = four             dollar
keycode   6 = five             percent
keycode   7 = six              ampersand
keycode   8 = seven            slash            braceleft
keycode   9 = eight            parenleft        bracketleft
keycode  10 = nine             parenright       bracketright
        control altgr keycode 10 = Control_bracketright

In the chapter above we arrived at the so called key codes. With a keymap as shown above we are now able to link a kernel key code to key symbols.

Lets look at some of the basics of the keymap definition. It is possible to link a key code not only to one but to up to 256 such key symbols. This is possible since each key code is not only regarded for itself but instead in context with so called modifier keys. This concept allows you to specify that pressing "a" on your keyboard will result in an "a" on the screen but yield an "A" if you press it while also holding the "Shift" modifier key down. Since there are eight such modifier keys present on a keyboard this results in 256 possible modifier key combinations. Actually it is highly unlikely that you will ever press more than three modifier keys at the same time but this concept is very useful for languages that have a more complicated symbol structure than ours.

#             Normal           Shift            AltGr           Strg
keycode   9 = eight            parenleft        bracketleft
keycode  10 = nine             parenright       bracketright
        control altgr keycode 10 = Control_bracketright

As visible from the excerpt above there are two ways how a key code gets combined with a modifier. In the first version the key code is specified first, followed by an equal sign and several columns that each define the result of combining this key code with a given modifiers. In principle you'd be able to have the full 256 columns here but most keymaps only have the first four that stand for "No modifier", "Shift", "AltGr" and "Strg".

If you need to define a more complex combination of modifiers you can do so using the structure given in the last line of the example above. Here the combination of modifiers is described first, followed by the key code. The part behind the equal sign then defines the result for exactly this type of key combination.

How to describe the outcome of a specific combination of activated keys? In principle you can use normal letters like "a" or "A" to describe the various results of the key strokes. But all the special characters have special names (like the dollar entry that would then result in the character "$"). You can look at the man page for key maps (man keymaps) but this is only a basic description. I found the full list of possible symbols only in the source code of the kbd package (http://freshmeat.net/projects/kbd) within src/ksyms.c.

Key maps in the kernel

Lets assume we are now able to define a keymap and store it in the running kernel using loadkeys. There will be some examples later that should clarify the use of loadkeys in case it is unclear here. But we want to get back to the Kernel first in order to provide some more background on the possible options we have with the loadkeys mechanism.

Lets look at key symbols. A key symbol is in fact a combination of a key type and a key value. Inside the kernel memory each key symbol is a 16 bit value and is handled within the kbd_keycode function in drivers/char/keyboard.c. If any of the four high bits is unset (< 0xf000) then the key symbol directly represents the corresponding unicode value and the key value will be returned as a unicode character (in UTF-8 format). The Basic Multilingual Plane defined for unicode reserves the region above 0xf000 for private definitions anyhow, so the kernel does not loose any space for symbol declarations.

Now in case the value is larger than 0xf000 then bits 9-12 represent the key type and bits 1-8 the key value.

The key type is being used to specify special key handlers inside the kernel. The following key types exist (defined in include/linux/keyboard.h):

#define KT_LATIN	0	/* we depend on this being zero */
#define KT_FN		1
#define KT_SPEC		2
#define KT_PAD		3
#define KT_DEAD		4
#define KT_CONS		5
#define KT_CUR		6
#define KT_SHIFT	7
#define KT_META		8
#define KT_ASCII	9
#define KT_LOCK		10
#define KT_LETTER	11	/* symbol that can be acted upon by CapsLock */
#define KT_SLOCK	12
#define KT_SPKUP       	14

Back to our definition of key symbols using loadkeys: This means that we are able to map specific key codes to key symbols that invoke special functions within the kernel. Each key type given above has its own special kernel function that will be executed if key code together with all active key modifiers maps to a key symbol that is marked as a such a special type.

The following are the possible functions:

LATIN will just return the key value as character and thus represents the old ASCII characters. This is not really necessary anymore.

FN will copy a string from a special table to the output. The table can hold a maximum of 256 entries.

echo 'string F100 = "hello world"' | loadkeys
echo 'keycode 30 = F100' | loadkeys

Pressing "a" now will result in the string "hello world". Revert back using

echo 'keycode 30 = a' | loadkeys

SPEC will call special kernel functions. These are the functions available:

#define FN_HANDLERS\
	fn_null, 	fn_enter,	fn_show_ptregs,	fn_show_mem,\
	fn_show_state,	fn_send_intr, 	fn_lastcons, 	fn_caps_toggle,\
	fn_num,		fn_hold, 	fn_scroll_forw,	fn_scroll_back,\
	fn_boot_it, 	fn_caps_on, 	fn_compose,	fn_SAK,\
	fn_dec_console, fn_inc_console, fn_spawn_con, 	fn_bare_num

You can reach these function using the following identifies for loadkeys:

static const char *spec_syms[] = {
        "VoidSymbol",
        "Return",
        "Show_Registers",
        "Show_Memory",
        "Show_State",
        "Break",
        "Last_Console",
        "Caps_Lock",
        "Num_Lock",
        "Scroll_Lock",
        "Scroll_Forward",
        "Scroll_Backward",
        "Boot",
        "Caps_On",
        "Compose",
        "SAK",
        "Decr_Console",
        "Incr_Console",
        "KeyboardSignal",
        "Bare_Num_Lock"
};

fn_enter will put character 13 in the queue (there is an option that will also append character 10 but I dont know where to activate that).

fn_show_ptregs dumps the register content to your kernel log.

fn_show_mem does the same with memory.

echo 'keycode 30 = Show_Memory' | loadkeys

Pressing "a" will now show echo the kernel memory information into your dmesg log. Revert back using:

echo 'keycode 30 = a' | loadkeys

fn_show_state does the same with state.

fn_send_intr sends a break signal (TTY_BREAK) to the terminal.

fn_lastcons switch to the last used console.

fn_caps_toggle toggle caps.

fn_num toggles num lock.

echo 'keycode 30 = Num_Lock' | loadkeys

Pressing "a" will now toggle the Num Lock status. Revert back using:

echo 'keycode 30 = a' | loadkeys

fn_hold stops the tty.

fn_scroll_forw scroll tty forward.

fn_scroll_back scroll tty backward.

fn_boot_it reboot the machine.

echo 'keycode 30 = Boot' | loadkeys

Pressing "a" will reboot the machine. Revert back using:

echo 'keycode 30 = a' | loadkeys

fn_caps_on turn caps on.

fn_compose marks the key as dead and will compose a special character together with the next key.

fn_SAK special attention key that can be used to call some disaster recovery functions in case the machine does not respond anymore.

fn_dec_console switch console backward.

fn_inc_console switch console forward.

fn_spawn_con spawn new console.

fn_bare_num switches the num lock flag without actually switching the internal state of the num lock flag.

PAD a key from the numerical pad. Will issue a character depending on the num lock state.

DEAD a dead key that will not directly result in a character but will be composed together with the next characters.

CONS switches to a specific console.

CUR cursor movement.

SHIFT handles modifier keys.

META handles the special meta key.

ASCII directly entering ascii codes. Usually this is mapped to your keypad and the modifier Alt as well as AltGr. Holding down Alt and pressing 4, 1 should result in ")" (Ascii code 41). The same result can be obtained with AltGr and 2, 9 (")" has hex code 0x29).

LOCK handles locking of modifier keys.

LETTER not implemented.

SLOCK no clue?

SPKUP call speakup subsystem.

Here is a list of the different modifier keys you can use:

include/linux/keyboard.h:

#define KG_SHIFT	0
#define KG_ALTGR	1
#define KG_CTRL		2
#define KG_ALT		3
#define KG_SHIFTL	4
#define KG_KANASHIFT	4
#define KG_SHIFTR	5
#define KG_CTRLL	6
#define KG_CTRLR	7
#define KG_CAPSSHIFT	8

This example shows the definition of modifier keys inside the kernel and the following shows the names you can use for these modifiers inside a loadkeys keymap definition.

static const char *shift_syms[] = {
        "Shift",
        "AltGr",
        "Control",
        "Alt",
        "ShiftL",
        "ShiftR",
        "CtrlL",
        "CtrlR",
        "CapsShift"
};

At this point you should know how to access the kernel specific functionality by using loadkeys.

Available key maps

The basic settings for all standard keyboards are already defined by standardized key maps also provided by the kbd package. They are usually available on a location like /usr/share/keymaps on your Linux system. So usually you will only choose one of these standard key maps plus maybe a few special maps. I'll just walk through these maps now, commenting them a little bit.

For my german keyboard I choose the de-latin1-nodeadkeys keymap. This keymap disables all dead keys so that the accents and tilde do not yield a special dead_xyz character.

The de-latin1-nodeadkeys map includes the de-latin1 key map. This map defines the standard german layout. It is based on the qwertz map that is included first. This defines a very basic a-z layout. It also includes definitions for the µ and @ symbols. Then comes a selection of special keys by including linux-with-alt-and-altgr. This key map mainly specifies the hex character composers, the Ctrl-Alt-Del reboot and includes linux-keys-bare.

linux-keys-bare defines function keys, console switching keys, the num pad, cursor keys, special edit keys (home, end,...) and some special system request and break codes.

After including linux-with-alt-and-altgr the de-latin1 map needs to redefine the key pad period to a comma since this is the correct separator in Germany. Then the euro2 map is included that binds the € sign to AltGr + e and the ¢ sign to AltGr + c.

Finally a compose map for latin1 (compose.latin1) is included.

It would be nice to actually have a compose key if we already included a compose map. This could be achieved using the ctrl key map which will bind the right "Ctrl" key as a compose key. But it also redefines "Caps lock" to the left control. So I did redefine the "Menu" key (second key on the right side of "Space") as a compose key.

keycode 127 = Compose

I am not quite sure about the backspace key map. It would replace Delete with Control_h. Do both mean the same thing?

I don't need the windowkeys key map since binding the special windows keys to console switching does not help in any way. The same effect is set to "Alt" + "cursor keys".

I did save my compose definition to a separate file:

echo "keycode 127 = Compose" | gzip -c > /usr/share/keymaps/i386/include/gunnar.map.gz

And now I can load my key map definition using

loadkeys de-latin1-nodeadkeys gunnar

Character display on the console

All the steps mentioned above result in keystrokes that are being available in unicode format (actually UTF-8). Now these characters need to be transferred to characters that can be displayed on screen.

We do obviously ignore the fact that there are programs involved in processing each keystroke before they actually appear on screen. But we can assume here that these programs accept UTF-8 encoded characters and also emit UTF-8 characters again. So we ignore this type of processing now.

What do we need in order to map certain UTF-8 character bytes to glyphs printed on the screen?

We need a font mapping and the tool setfont. On a standard system that has the kbd package installed the font mappings will be available in /usr/share/consolefonts. You'll see a lot of fonts in there that have the ending *.psfu.gz. These are standard unicode fonts and associate the glyph bitmaps directly with unicode values. There are also older fonts without unicode support but they are deprecated and it is expected that at some point all fonts of the kbd package will be unicode fonts.

By default setfont will select one of the default fonts (e.g. default8x16). These are okay but do not support many different characters.

You can easily check the different character support on your console by downloading this UTF-8 demo file and trying to look at it using less. With default8x16 you should see that many characters in the demo file actually result in a "?" glyph on the screen.

You won't find a font mapping that will allow to display all characters in the test file since a console font mapping can provide at most 512 different character bitmaps. So you need to choose a font mapping that matches your language settings best.

For the western world I would suggest the LatArCyrHeb-16 font mapping. It should slightly improve the display of the UTF-8 demo file.

Another good option is the terminus font mapping available here.

Once you chose your font mapping you can activate it for the current console by issuing something like the following command:

setfont ter-v12n

The €-Symbol on the console

If you use either the LatArCyrHeb-16 or the terminus font on the console you will probably not get the €-Symbol on the screen when pressing AltGr + e. Instead you will probably see this sign: ¤. You can find out more about this symbol on this page. It means "currency" but is not really being used. Non-unicode fonts may directly map "currency" to the €-Symbol but the correct unicode value is actually U+20AC.

The following loadkeys mapping fixes the problem:

altgr keycode 18 = U+20AC

Mapping keystrokes for the X server

The X server does not really care about the processing of key strokes provided by the linux kernel. Once the server starts it sets the console into raw mode which instructs the kernel to revert all its key code generation back to scancodes. At least this seems to be the standard situation when using the X kbd driver.

I guess this has historical reasons even though it does not really seem to be logical. Anyhow the X server recognizes all the keys of my keyboard without complaining about unkown keys.

The X server maps each scancode to a key code (I don't know exactly how this is done) and the program xev can be used to display the key codes.

Now I can use xmodmap to map the key codes that are currently unmapped to some useful actions. The ~/.Xmodmap file will hold all necessary information:

## Eigene Dateien
keycode 239 = XF86Documents
## Eigene Bilder
keycode 187 = XF86Pictures
## Eigene Musik
keycode 118 = XF86Music
## Ton aus
keycode 160 = XF86AudioMute
## Play
keycode 162 = XF86AudioPlay
## Stop
keycode 164 = XF86AudioStop
## Lauter
keycode 176 = XF86AudioRaiseVolume
## Leiser
keycode 174 = XF86AudioLowerVolume
## Voriger Titel
keycode 144 = XF86AudioPrev
## Nächster Titel
keycode 153 = XF86AudioNext
## Medien
keycode 237 = XF86AudioMedia
## E-Mail
keycode 236 = XF86Mail
## Startseite
keycode 178 = XF86HomePage
## Messenger
keycode 121 = XF86Messenger
## Rechner
keycode 161 = XF86Calculater
## Abmelden
keycode 209 = XF86LogOff
## Standby
keycode 223 = XF86Standby
## Hilfe
keycode 245 = XF86LightBulb
## Rückg
keycode 135 = XF86History
## Wiederherstell
keycode 133 = XF86Refresh
## Neu
keycode 198 = XF86New
## Öffnen
keycode 191 = XF86Open
## Schliessen
keycode 175 = XF86Close
## Antwort
keycode 159 = XF86Reply
## Weiterleiten
keycode 151 = XF86MailForward
## Senden
keycode 199 = XF86Send
## Rechtschreibung
keycode 171 = XF86Spell
## Speichern
keycode 172 = XF86Save
## Drucken
keycode 185 = XF86Display

This file needs to be sourced by xmodmap for each session start, so I add it to ~/.xprofile (KDM will source it on starting the session):

/usr/X11R6/bin/xmodmap $HOME/.Xmodmap

Alternative: Lineakd

Lineakd is a background daemon that listens to the X server events and reacts to the special keys. This has the advantage that you can define actions independant of the window manager you actually use. The disadvantage is that this prevents you from using special features of your window manager. So it is a matter of preference.

TODO

... more to do ... ( ... tty ... network ...)

Links

  • Multimedia Keys on Gentoo
  • UTF 8 on Gentoo
  • Hints from Console to terms and bash
Back to Personal Wiki
Creative Commons-Lizenzvertrag
The content of this site is licensed under a Creative Commons 2.5 License [attribution, share-alike]