Subject: [PATCH] Arrow keys on USB keyboards

From: Geert Uytterhoeven <[email-masked]>
Date: Fri, 23 May 2003 15:47:29 +0200 (MEST)

Hi Ralf,

This patch fixes the arrow keys (and all other keys that generate E0/E1
scancode prefixes on AT keyboards) by adding support for E0/E1 scancode
prefixes to the dummy keyboard driver if CONFIG_INPUT=y.

Rationale: When using the new input layer (i.e. with a USB keyboard or a custom
input device), the input layer relies on kbd_translate() in the low-level
keyboard driver to convert from AT-style scancodes to keycodes. If you don't
have a PS/2 keyboard interface and don't compile in the PS/2 keyboard driver,
you have to enable the dummy keyboard driver, which naively assumes that
keycodes and scancodes are interchangeable. This is correct if you do not have
a keyboard, but fails for prefixed scancodes if you do have a keyboard which
uses the new input layer.

--- linux-mips-2.4.x/drivers/char/dummy_keyb.c	Tue Apr  1 16:33:31 2003
+++ linux/drivers/char/dummy_keyb.c	Wed Apr 30 08:12:19 2003
@@ -23,9 +23,12 @@
  * CONFIG_VT.
  *
  */
+
+#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/input.h>
 
 void kbd_leds(unsigned char leds)
 {
@@ -41,6 +44,84 @@
 	return scancode;
 }
 
+#ifdef CONFIG_INPUT
+static unsigned char e0_keys[128] = {
+	0, 0, 0, KEY_KPCOMMA, 0, KEY_INTL3, 0, 0,		/* 0x00-0x07 */
+	0, 0, 0, 0, KEY_LANG1, KEY_LANG2, 0, 0,			/* 0x08-0x0f */
+	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x10-0x17 */
+	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, KEY_VOLUMEUP, 0,/* 0x18-0x1f */
+	0, 0, 0, 0, 0, KEY_VOLUMEDOWN, KEY_MUTE, 0,		/* 0x20-0x27 */
+	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x28-0x2f */
+	0, 0, 0, 0, 0, KEY_KPSLASH, 0, KEY_SYSRQ,		/* 0x30-0x37 */
+	KEY_RIGHTALT, KEY_BRIGHTNESSUP, KEY_BRIGHTNESSDOWN, 
+		KEY_EJECTCD, 0, 0, 0, 0,			/* 0x38-0x3f */
+	0, 0, 0, 0, 0, 0, 0, KEY_HOME,				/* 0x40-0x47 */
+	KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48-0x4f */
+	KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50-0x57 */
+	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, KEY_POWER, 0, /* 0x58-0x5f */
+	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x60-0x67 */
+	0, 0, 0, 0, 0, 0, 0, KEY_MACRO,				/* 0x68-0x6f */
+	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x70-0x77 */
+	0, 0, 0, 0, 0, 0, 0, 0					/* 0x78-0x7f */
+};
+
+int kbd_translate(unsigned char scancode, unsigned char *keycode,
+	char raw_mode)
+{
+	/* This code was copied from char/pc_keyb.c and will be
+	 * superflous when the input layer is fully integrated.
+	 * We don't need the high_keys handling, so this part
+	 * has been removed.
+	 */
+	static int prev_scancode = 0;
+
+	/* special prefix scancodes.. */
+	if (scancode == 0xe0 || scancode == 0xe1) {
+		prev_scancode = scancode;
+		return 0;
+	}
+
+	scancode &= 0x7f;
+
+	if (prev_scancode) {
+		if (prev_scancode != 0xe0) {
+			if (prev_scancode == 0xe1 && scancode == 0x1d) {
+				prev_scancode = 0x100;
+				return 0;
+			} else if (prev_scancode == 0x100 && scancode == 0x45) {
+				*keycode = KEY_PAUSE;
+				prev_scancode = 0;
+			} else {
+				if (!raw_mode)
+					printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
+				prev_scancode = 0;
+				return 0;
+			}
+		} else {
+			prev_scancode = 0;
+			if (scancode == 0x2a || scancode == 0x36)
+				return 0;
+		}
+		if (e0_keys[scancode])
+			*keycode = e0_keys[scancode];
+		else {
+			if (!raw_mode)
+				printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
+				       scancode);
+			return 0;
+		}
+	} else {
+		switch (scancode) {
+		case  91: scancode = KEY_LINEFEED; break;
+		case  92: scancode = KEY_KPEQUAL; break;
+		case 125: scancode = KEY_INTL1; break;
+		}
+		*keycode = scancode;
+	}
+	return 1;
+}
+
+#else
 int kbd_translate(unsigned char scancode, unsigned char *keycode,
 	char raw_mode)
 {
@@ -48,6 +129,7 @@
 
 	return 1;
 }
+#endif
 
 char kbd_unexpected_up(unsigned char keycode)
 {

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email-masked]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Fri, 23 May 2003 16:05:43 +0200 (MET DST)

> uses the new input layer.

 Hmm, if the PC/AT keyboard translation is needed by other devices beside
pc_keyb.c, then why isn't the common part put into a separate file to be
used by all devices depending on this translation as needed?  I think
dummy_keyb.c should be kept plain and simple as it is now. 

-- 
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: [email-masked], PGP key available        +

---

From: Geert Uytterhoeven <[email-masked]>
Date: Fri, 23 May 2003 16:15:00 +0200 (MEST)

> dummy_keyb.c should be kept plain and simple as it is now. 

In 2.5.x it's (probably) that way. In 2.4.x the input layer is more like a hack
to get USB working. On PCs, you always compile in both PS/2 keyboard and USB
keyboard support, so it always works. The dummy_keyb.c is used on MIPS only.
BTW, I forgot to mention: I just copied what the PPC guys do on PowerMac, cfr.
drivers/macintosh/mac_hid.c.

But it indeed makes sense to move kbd_translate() into the input layer itself.
Vojtech, what do you think?

Gr{oetje,eeting}s,

						Geert

---

From: Ralf Baechle <[email-masked]>
Date: Tue, 27 May 2003 07:46:49 +0200

> dummy_keyb.c should be kept plain and simple as it is now. 

You're right but for 2.4 this looks like an acceptable solution for now so
I'm going to apply this until somebody comes up with a better solution.

  Ralf

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Tue, 27 May 2003 15:15:33 +0200 (MET DST)

> I'm going to apply this until somebody comes up with a better solution.

 Hmm, as I've understood that's a 2.4-only problem as 2.5 has it solved
differently.  And I do think the translation really belong to the drivers
that use it -- why can't it be included with the USB keyboard driver or as
a library file?  Why an unrelated driver has to be cluttered?

---

From: Geert Uytterhoeven <[email-masked]>
Date: Tue, 27 May 2003 15:29:54 +0200 (MEST)

> a library file?  Why an unrelated driver has to be cluttered? 

It's not really used by a driver, but by the input subsystem itself. You could
add the translation to drivers/char/keyboard.c, but then it will break if you
use both the input subsystem (e.g. USB keyboard) and some other non-PS/2
keyboard driver.

Gr{oetje,eeting}s,

						Geert

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Tue, 27 May 2003 15:50:59 +0200 (MET DST)

> use both the input subsystem (e.g. USB keyboard) and some other non-PS/2

 I don't understand -- the scancode mapping is specific to a keyboard
type.  Both PC/AT and USB keyboards may use the same scancodes by chance,
but others have different ones.  So how can the input subsystem need a
PC/AT specific mapping?  Adding the table to drivers/char/keyboard.c
certainly makes no sense as the file is meant to be generic. 

 BTW, the PS/2 mapping is yet different and doesn't use prefixes.

---

From: Geert Uytterhoeven <[email-masked]>
Date: Tue, 27 May 2003 15:57:01 +0200 (MEST)

> certainly makes no sense as the file is meant to be generic. 

Scancode mapping is indeed specific to the keyboard type.

However, the input subsystem converts Linux input keycodes (cfr.
<linux/input.h>) to PC/AT scancodes, and feeds them to handle_scancode() in
drivers/char/keyboard.c. handle_scancode() calls kbd_translate() to translate
scancodes to keycodes, which is keyboard driver specific.

Since the input subsystem feeds PC/AT scancodes to handle_scancode(),
kbd_translate() needs to know about PC/AT scancodes. For the PS/2 keyboard
driver this is OK, for the dummy keyboard driver this is OK after my patch, but
for any other keyboard driver this will not work.

Gr{oetje,eeting}s,

						Geert

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Tue, 27 May 2003 16:16:22 +0200 (MET DST)

> scancodes to keycodes, which is keyboard driver specific.

 Oh what a mess! -- why does it do so?  Since as I understand these input
keycodes are already translated from scancodes, why do they need to be
translated "back" to PC/AT scancodes (possibly with private inventions for
keys that do not exist on PC/AT or PS/2 keyboards) only to be translated
to medium-raw keycodes (for a total number of four translations from an
original scancode to a final cooked sequence)?  Why can't the input layer
provide its own kbd_translate() function translating directly from input
keycodes to medium-raw keycodes (or why the input keycodes are different
from medium-raw keycodes at all)?

 After all it looks like the input layer can be treated like a virtual
keyboard driver itself. 

> for any other keyboard driver this will not work.

 Why can't the input layer be fixed instead?  Given the interface between
the input layer and drivers/char/keyboard.c is internal the impact on
backend drivers should be nonexistent, hence no incompatibility problem
for 2.4.