Add keyboard layouts support.

* Makefile.util.def (grub-mklayout): New file.
	(grub-kbdcomp): New script.
	* grub-core/Makefile.am (KERNEL_HEADER_FILES) [COND_mips_yeeloong]:
	Add keyboard_layouts.h.
	* grub-core/Makefile.core.def (kernel): Add commands/keylayouts.c and
	commands/boot.c on yeeloong.
	(keylayouts): New module.
	* grub-core/bus/usb/ohci.c
	* grub-core/bus/usb/uhci.c
	* grub-core/bus/usb/usbhub.c (rescan): New variable.
	(grub_usb_add_hub): Poll interrupt pipe for device handling.
	(attach_root_port): Likewise.
	(poll_nonroot_hub): Likewise.
	(grub_usb_poll_devices): Likewise.
	(detach_device): Close transfer.
	* grub-core/bus/usb/usbtrans.c (grub_usb_execute_and_wait_transfer): New
	function.
	(grub_usb_bulk_setup_readwrite): Likewise.
	(grub_usb_bulk_finish_readwrite): Likewise.
	* grub-core/commands/keylayouts.c: New file.
	* grub-core/commands/keystatus.c (grub_getkeystatus): New function.
	* grub-core/commands/menuentry.c (hotkey_aliases): All several new
	aliases.
	* grub-core/term/at_keyboard.c: Restructured to use keylayouts and
	support scancode 2.
	* grub-core/term/usb_keyboard.c: Restructured to use keylayouts.
	* include/grub/keyboard_layouts.h: New file.
	* util/grub-mklayout.c: New file.
	* util/grub-kbdcomp.in: Likewise.

	Also-By: Aleš Nesrsta <starous@volny.cz>

	Also-By: Vladimir Serbinenko <phcoder@gmail.com>
This commit is contained in:
Carles Pina i Estany 2010-09-19 01:01:35 +02:00 committed by Vladimir 'phcoder' Serbinenko
commit 1a9130dd3f
41 changed files with 3067 additions and 1459 deletions

View file

@ -22,18 +22,13 @@
#include <grub/cpu/io.h>
#include <grub/misc.h>
#include <grub/term.h>
#include <grub/keyboard_layouts.h>
#include <grub/time.h>
#include <grub/loader.h>
static short at_keyboard_status = 0;
static int pending_key = -1;
#define KEYBOARD_STATUS_SHIFT_L (1 << 0)
#define KEYBOARD_STATUS_SHIFT_R (1 << 1)
#define KEYBOARD_STATUS_ALT_L (1 << 2)
#define KEYBOARD_STATUS_ALT_R (1 << 3)
#define KEYBOARD_STATUS_CTRL_L (1 << 4)
#define KEYBOARD_STATUS_CTRL_R (1 << 5)
#define KEYBOARD_STATUS_CAPS_LOCK (1 << 6)
#define KEYBOARD_STATUS_NUM_LOCK (1 << 7)
static int e0_received = 0;
static int f0_received = 0;
static grub_uint8_t led_status;
@ -41,36 +36,179 @@ static grub_uint8_t led_status;
#define KEYBOARD_LED_NUM (1 << 1)
#define KEYBOARD_LED_CAPS (1 << 2)
static char keyboard_map[128] =
{
'\0', GRUB_TERM_ESC, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', GRUB_TERM_BACKSPACE, GRUB_TERM_TAB,
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', '\0', 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'', '`', '\0', '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', '\0', '*',
'\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', GRUB_TERM_HOME,
GRUB_TERM_UP, GRUB_TERM_NPAGE, '-', GRUB_TERM_LEFT, '\0', GRUB_TERM_RIGHT, '+', GRUB_TERM_END,
GRUB_TERM_DOWN, GRUB_TERM_PPAGE, '\0', GRUB_TERM_DC, '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', OLPC_UP, OLPC_DOWN, OLPC_LEFT,
OLPC_RIGHT
};
static char keyboard_map_shift[128] =
{
'\0', '\0', '!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '\0', '\0',
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', '{', '}', '\n', '\0', 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
'\"', '~', '\0', '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?'
};
static grub_uint8_t grub_keyboard_controller_orig;
static grub_uint8_t grub_keyboard_orig_set;
static grub_uint8_t current_set;
static const grub_uint8_t set1_mapping[128] =
{
/* 0x00 */ 0 /* Unused */, GRUB_KEYBOARD_KEY_ESCAPE,
/* 0x02 */ GRUB_KEYBOARD_KEY_1, GRUB_KEYBOARD_KEY_2,
/* 0x04 */ GRUB_KEYBOARD_KEY_3, GRUB_KEYBOARD_KEY_4,
/* 0x06 */ GRUB_KEYBOARD_KEY_5, GRUB_KEYBOARD_KEY_6,
/* 0x08 */ GRUB_KEYBOARD_KEY_7, GRUB_KEYBOARD_KEY_8,
/* 0x0a */ GRUB_KEYBOARD_KEY_9, GRUB_KEYBOARD_KEY_0,
/* 0x0c */ GRUB_KEYBOARD_KEY_DASH, GRUB_KEYBOARD_KEY_EQUAL,
/* 0x0e */ GRUB_KEYBOARD_KEY_BACKSPACE, GRUB_KEYBOARD_KEY_TAB,
/* 0x10 */ GRUB_KEYBOARD_KEY_Q, GRUB_KEYBOARD_KEY_W,
/* 0x12 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_R,
/* 0x14 */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_Y,
/* 0x16 */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_I,
/* 0x18 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_P,
/* 0x1a */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_RBRACKET,
/* 0x1c */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_LEFT_CTRL,
/* 0x1e */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_S,
/* 0x20 */ GRUB_KEYBOARD_KEY_D, GRUB_KEYBOARD_KEY_F,
/* 0x22 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_H,
/* 0x24 */ GRUB_KEYBOARD_KEY_J, GRUB_KEYBOARD_KEY_K,
/* 0x26 */ GRUB_KEYBOARD_KEY_L, GRUB_KEYBOARD_KEY_SEMICOLON,
/* 0x28 */ GRUB_KEYBOARD_KEY_DQUOTE, GRUB_KEYBOARD_KEY_RQUOTE,
/* 0x2a */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, GRUB_KEYBOARD_KEY_BACKSLASH,
/* 0x2c */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_X,
/* 0x2e */ GRUB_KEYBOARD_KEY_C, GRUB_KEYBOARD_KEY_V,
/* 0x30 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_N,
/* 0x32 */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_COMMA,
/* 0x34 */ GRUB_KEYBOARD_KEY_DOT, GRUB_KEYBOARD_KEY_SLASH,
/* 0x36 */ GRUB_KEYBOARD_KEY_RIGHT_SHIFT, GRUB_KEYBOARD_KEY_NUMMUL,
/* 0x38 */ GRUB_KEYBOARD_KEY_LEFT_ALT, GRUB_KEYBOARD_KEY_SPACE,
/* 0x3a */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_F1,
/* 0x3c */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F3,
/* 0x3e */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_F5,
/* 0x40 */ GRUB_KEYBOARD_KEY_F6, GRUB_KEYBOARD_KEY_F7,
/* 0x42 */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F9,
/* 0x44 */ GRUB_KEYBOARD_KEY_F10, GRUB_KEYBOARD_KEY_NUM_LOCK,
/* 0x46 */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, GRUB_KEYBOARD_KEY_NUM7,
/* 0x48 */ GRUB_KEYBOARD_KEY_NUM8, GRUB_KEYBOARD_KEY_NUM9,
/* 0x4a */ GRUB_KEYBOARD_KEY_NUMMINUS, GRUB_KEYBOARD_KEY_NUM4,
/* 0x4c */ GRUB_KEYBOARD_KEY_NUM5, GRUB_KEYBOARD_KEY_NUM6,
/* 0x4e */ GRUB_KEYBOARD_KEY_NUMPLUS, GRUB_KEYBOARD_KEY_NUM1,
/* 0x50 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM3,
/* 0x52 */ GRUB_KEYBOARD_KEY_NUMDOT, GRUB_KEYBOARD_KEY_NUMDOT,
/* 0x54 */ 0, 0,
/* 0x56 */ GRUB_KEYBOARD_KEY_102ND, GRUB_KEYBOARD_KEY_F11,
/* 0x58 */ GRUB_KEYBOARD_KEY_F12, 0,
/* 0x5a */ 0, 0,
/* 0x5c */ 0, 0,
/* 0x5e */ 0, 0,
/* 0x60 */ 0, 0,
/* 0x62 */ 0, 0,
/* OLPC keys. Just mapped to normal keys. */
/* 0x64 */ 0, GRUB_KEYBOARD_KEY_UP,
/* 0x66 */ GRUB_KEYBOARD_KEY_DOWN, GRUB_KEYBOARD_KEY_LEFT,
/* 0x68 */ GRUB_KEYBOARD_KEY_RIGHT
};
static const struct
{
grub_uint8_t from, to;
} set1_e0_mapping[] =
{
{0x1c, GRUB_KEYBOARD_KEY_NUMENTER},
{0x1d, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
{0x35, GRUB_KEYBOARD_KEY_NUMSLASH },
{0x38, GRUB_KEYBOARD_KEY_RIGHT_ALT},
{0x47, GRUB_KEYBOARD_KEY_HOME},
{0x48, GRUB_KEYBOARD_KEY_UP},
{0x49, GRUB_KEYBOARD_KEY_NPAGE},
{0x4b, GRUB_KEYBOARD_KEY_LEFT},
{0x4d, GRUB_KEYBOARD_KEY_RIGHT},
{0x4f, GRUB_KEYBOARD_KEY_END},
{0x50, GRUB_KEYBOARD_KEY_DOWN},
{0x51, GRUB_KEYBOARD_KEY_PPAGE},
{0x52, GRUB_KEYBOARD_KEY_INSERT},
{0x53, GRUB_KEYBOARD_KEY_DELETE},
};
static const grub_uint8_t set2_mapping[256] =
{
/* 0x00 */ 0, GRUB_KEYBOARD_KEY_F9,
/* 0x02 */ 0, GRUB_KEYBOARD_KEY_F5,
/* 0x04 */ GRUB_KEYBOARD_KEY_F3, GRUB_KEYBOARD_KEY_F1,
/* 0x06 */ GRUB_KEYBOARD_KEY_F2, GRUB_KEYBOARD_KEY_F12,
/* 0x08 */ 0, GRUB_KEYBOARD_KEY_F10,
/* 0x0a */ GRUB_KEYBOARD_KEY_F8, GRUB_KEYBOARD_KEY_F6,
/* 0x0c */ GRUB_KEYBOARD_KEY_F4, GRUB_KEYBOARD_KEY_TAB,
/* 0x0e */ GRUB_KEYBOARD_KEY_RQUOTE, 0,
/* 0x10 */ 0, GRUB_KEYBOARD_KEY_LEFT_ALT,
/* 0x12 */ GRUB_KEYBOARD_KEY_LEFT_SHIFT, 0,
/* 0x14 */ GRUB_KEYBOARD_KEY_LEFT_CTRL, GRUB_KEYBOARD_KEY_Q,
/* 0x16 */ GRUB_KEYBOARD_KEY_1, 0,
/* 0x18 */ 0, 0,
/* 0x1a */ GRUB_KEYBOARD_KEY_Z, GRUB_KEYBOARD_KEY_S,
/* 0x1c */ GRUB_KEYBOARD_KEY_A, GRUB_KEYBOARD_KEY_W,
/* 0x1e */ GRUB_KEYBOARD_KEY_2, 0,
/* 0x20 */ 0, GRUB_KEYBOARD_KEY_C,
/* 0x22 */ GRUB_KEYBOARD_KEY_X, GRUB_KEYBOARD_KEY_D,
/* 0x24 */ GRUB_KEYBOARD_KEY_E, GRUB_KEYBOARD_KEY_4,
/* 0x26 */ GRUB_KEYBOARD_KEY_3, 0,
/* 0x28 */ 0, GRUB_KEYBOARD_KEY_SPACE,
/* 0x2a */ GRUB_KEYBOARD_KEY_V, GRUB_KEYBOARD_KEY_F,
/* 0x2c */ GRUB_KEYBOARD_KEY_T, GRUB_KEYBOARD_KEY_R,
/* 0x2e */ GRUB_KEYBOARD_KEY_5, 0,
/* 0x30 */ 0, GRUB_KEYBOARD_KEY_N,
/* 0x32 */ GRUB_KEYBOARD_KEY_B, GRUB_KEYBOARD_KEY_H,
/* 0x34 */ GRUB_KEYBOARD_KEY_G, GRUB_KEYBOARD_KEY_Y,
/* 0x36 */ GRUB_KEYBOARD_KEY_6, 0,
/* 0x38 */ 0, 0,
/* 0x3a */ GRUB_KEYBOARD_KEY_M, GRUB_KEYBOARD_KEY_J,
/* 0x3c */ GRUB_KEYBOARD_KEY_U, GRUB_KEYBOARD_KEY_7,
/* 0x3e */ GRUB_KEYBOARD_KEY_8, 0,
/* 0x40 */ 0, GRUB_KEYBOARD_KEY_COMMA,
/* 0x42 */ GRUB_KEYBOARD_KEY_K, GRUB_KEYBOARD_KEY_I,
/* 0x44 */ GRUB_KEYBOARD_KEY_O, GRUB_KEYBOARD_KEY_0,
/* 0x46 */ GRUB_KEYBOARD_KEY_9, 0,
/* 0x48 */ 0, GRUB_KEYBOARD_KEY_DOT,
/* 0x4a */ GRUB_KEYBOARD_KEY_SLASH, GRUB_KEYBOARD_KEY_L,
/* 0x4c */ GRUB_KEYBOARD_KEY_SEMICOLON, GRUB_KEYBOARD_KEY_P,
/* 0x4e */ GRUB_KEYBOARD_KEY_DASH, 0,
/* 0x50 */ 0, 0,
/* 0x52 */ GRUB_KEYBOARD_KEY_DQUOTE, 0,
/* 0x54 */ GRUB_KEYBOARD_KEY_LBRACKET, GRUB_KEYBOARD_KEY_EQUAL,
/* 0x56 */ 0, 0,
/* 0x58 */ GRUB_KEYBOARD_KEY_CAPS_LOCK, GRUB_KEYBOARD_KEY_RIGHT_SHIFT,
/* 0x5a */ GRUB_KEYBOARD_KEY_ENTER, GRUB_KEYBOARD_KEY_RBRACKET,
/* 0x5c */ 0, GRUB_KEYBOARD_KEY_BACKSLASH,
/* 0x5e */ 0, 0,
/* 0x60 */ 0, GRUB_KEYBOARD_KEY_102ND,
/* 0x62 */ 0, 0,
/* 0x64 */ 0, 0,
/* 0x66 */ GRUB_KEYBOARD_KEY_BACKSPACE, 0,
/* 0x68 */ 0, GRUB_KEYBOARD_KEY_NUM1,
/* 0x6a */ 0, GRUB_KEYBOARD_KEY_NUM4,
/* 0x6c */ GRUB_KEYBOARD_KEY_NUM7, 0,
/* 0x6e */ 0, 0,
/* 0x70 */ GRUB_KEYBOARD_KEY_NUMDOT, GRUB_KEYBOARD_KEY_NUM0,
/* 0x72 */ GRUB_KEYBOARD_KEY_NUM2, GRUB_KEYBOARD_KEY_NUM5,
/* 0x74 */ GRUB_KEYBOARD_KEY_NUM6, GRUB_KEYBOARD_KEY_NUM8,
/* 0x76 */ GRUB_KEYBOARD_KEY_ESCAPE, GRUB_KEYBOARD_KEY_NUM_LOCK,
/* 0x78 */ GRUB_KEYBOARD_KEY_F11, GRUB_KEYBOARD_KEY_NUMPLUS,
/* 0x7a */ GRUB_KEYBOARD_KEY_NUM3, GRUB_KEYBOARD_KEY_NUMMINUS,
/* 0x7c */ GRUB_KEYBOARD_KEY_NUMMUL, GRUB_KEYBOARD_KEY_NUM9,
/* 0x7e */ GRUB_KEYBOARD_KEY_SCROLL_LOCK, 0,
/* 0x80 */ 0, 0,
/* 0x82 */ 0, GRUB_KEYBOARD_KEY_F7,
};
static const struct
{
grub_uint8_t from, to;
} set2_e0_mapping[] =
{
{0x11, GRUB_KEYBOARD_KEY_RIGHT_ALT},
{0x14, GRUB_KEYBOARD_KEY_RIGHT_CTRL},
{0x4a, GRUB_KEYBOARD_KEY_NUMSLASH},
{0x5a, GRUB_KEYBOARD_KEY_NUMENTER},
{0x69, GRUB_KEYBOARD_KEY_END},
{0x6b, GRUB_KEYBOARD_KEY_LEFT},
{0x6c, GRUB_KEYBOARD_KEY_HOME},
{0x70, GRUB_KEYBOARD_KEY_INSERT},
{0x71, GRUB_KEYBOARD_KEY_DELETE},
{0x72, GRUB_KEYBOARD_KEY_DOWN},
{0x74, GRUB_KEYBOARD_KEY_RIGHT},
{0x75, GRUB_KEYBOARD_KEY_UP},
{0x7a, GRUB_KEYBOARD_KEY_NPAGE},
{0x7d, GRUB_KEYBOARD_KEY_PPAGE},
};
static void
keyboard_controller_wait_until_ready (void)
@ -78,22 +216,133 @@ keyboard_controller_wait_until_ready (void)
while (! KEYBOARD_COMMAND_ISREADY (grub_inb (KEYBOARD_REG_STATUS)));
}
static grub_uint8_t
wait_ack (void)
{
grub_uint64_t endtime;
grub_uint8_t ack;
endtime = grub_get_time_ms () + 20;
do
ack = grub_inb (KEYBOARD_REG_DATA);
while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
&& grub_get_time_ms () < endtime);
return ack;
}
static int
at_command (grub_uint8_t data)
{
unsigned i;
for (i = 0; i < GRUB_AT_TRIES; i++)
{
grub_uint8_t ack;
keyboard_controller_wait_until_ready ();
grub_outb (data, KEYBOARD_REG_STATUS);
ack = wait_ack ();
if (ack == GRUB_AT_NACK)
continue;
if (ack == GRUB_AT_ACK)
break;
return 0;
}
return (i != GRUB_AT_TRIES);
}
static void
grub_keyboard_controller_write (grub_uint8_t c)
{
at_command (KEYBOARD_COMMAND_WRITE);
keyboard_controller_wait_until_ready ();
grub_outb (KEYBOARD_COMMAND_WRITE, KEYBOARD_REG_STATUS);
grub_outb (c, KEYBOARD_REG_DATA);
}
static grub_uint8_t
grub_keyboard_controller_read (void)
{
at_command (KEYBOARD_COMMAND_READ);
keyboard_controller_wait_until_ready ();
grub_outb (KEYBOARD_COMMAND_READ, KEYBOARD_REG_STATUS);
return grub_inb (KEYBOARD_REG_DATA);
}
static int
write_mode (int mode)
{
unsigned i;
for (i = 0; i < GRUB_AT_TRIES; i++)
{
grub_uint8_t ack;
keyboard_controller_wait_until_ready ();
grub_outb (0xf0, KEYBOARD_REG_DATA);
keyboard_controller_wait_until_ready ();
grub_outb (mode, KEYBOARD_REG_DATA);
keyboard_controller_wait_until_ready ();
ack = wait_ack ();
if (ack == GRUB_AT_NACK)
continue;
if (ack == GRUB_AT_ACK)
break;
return 0;
}
return (i != GRUB_AT_TRIES);
}
static int
query_mode (void)
{
grub_uint8_t ret;
int e;
e = write_mode (0);
if (!e)
return 0;
keyboard_controller_wait_until_ready ();
do
ret = grub_inb (KEYBOARD_REG_DATA);
while (ret == GRUB_AT_ACK);
/* QEMU translates the set even in no-translate mode. */
if (ret == 0x43 || ret == 1)
return 1;
if (ret == 0x41 || ret == 2)
return 2;
if (ret == 0x3f || ret == 3)
return 3;
return 0;
}
static void
set_scancodes (void)
{
/* You must have visited computer museum. Keyboard without scancode set
knowledge. Assume XT. */
if (!grub_keyboard_orig_set)
{
grub_dprintf ("atkeyb", "No sets support assumed\n");
current_set = 1;
return;
}
grub_keyboard_controller_write (grub_keyboard_controller_orig
& ~KEYBOARD_AT_TRANSLATE);
write_mode (2);
current_set = query_mode ();
grub_dprintf ("atkeyb", "returned set %d\n", current_set);
if (current_set == 2)
return;
write_mode (1);
current_set = query_mode ();
grub_dprintf ("atkeyb", "returned set %d\n", current_set);
if (current_set == 1)
return;
grub_printf ("No supported scancode set found\n");
}
static void
keyboard_controller_led (grub_uint8_t leds)
{
@ -103,195 +352,271 @@ keyboard_controller_led (grub_uint8_t leds)
grub_outb (leds & 0x7, KEYBOARD_REG_DATA);
}
static int
fetch_key (int *is_break)
{
int was_ext = 0;
grub_uint8_t at_key;
int ret = 0;
if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
return -1;
at_key = grub_inb (KEYBOARD_REG_DATA);
if (at_key == 0xe0)
{
e0_received = 1;
return -1;
}
if ((current_set == 2 || current_set == 3) && at_key == 0xf0)
{
f0_received = 1;
return -1;
}
/* Setting LEDs may generate ACKs. */
if (at_key == GRUB_AT_ACK)
return -1;
was_ext = e0_received;
e0_received = 0;
switch (current_set)
{
case 1:
*is_break = !!(at_key & 0x80);
if (!was_ext)
ret = set1_mapping[at_key & 0x7f];
else
{
unsigned i;
for (i = 0; i < ARRAY_SIZE (set1_e0_mapping); i++)
if (set1_e0_mapping[i].from == (at_key & 0x7f))
{
ret = set1_e0_mapping[i].to;
break;
}
}
break;
case 2:
*is_break = f0_received;
f0_received = 0;
if (!was_ext)
ret = set2_mapping[at_key];
else
{
unsigned i;
for (i = 0; i < ARRAY_SIZE (set2_e0_mapping); i++)
if (set2_e0_mapping[i].from == at_key)
{
ret = set2_e0_mapping[i].to;
break;
}
}
break;
default:
return -1;
}
if (!ret)
{
if (was_ext)
grub_printf ("Unknown key 0xe0+0x%02x from set %d\n",
at_key, current_set);
else
grub_printf ("Unknown key 0x%02x from set %d\n",
at_key, current_set);
return -1;
}
return ret;
}
/* FIXME: This should become an interrupt service routine. For now
it's just used to catch events from control keys. */
static void
grub_keyboard_isr (char key)
static int
grub_keyboard_isr (grub_keyboard_key_t key, int is_break)
{
char is_make = KEYBOARD_ISMAKE (key);
key = KEYBOARD_SCANCODE (key);
if (is_make)
if (!is_break)
switch (key)
{
case SHIFT_L:
at_keyboard_status |= KEYBOARD_STATUS_SHIFT_L;
break;
case SHIFT_R:
at_keyboard_status |= KEYBOARD_STATUS_SHIFT_R;
break;
case CTRL:
at_keyboard_status |= KEYBOARD_STATUS_CTRL_L;
break;
case ALT:
at_keyboard_status |= KEYBOARD_STATUS_ALT_L;
break;
default:
/* Skip grub_dprintf. */
return;
case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
at_keyboard_status |= GRUB_TERM_STATUS_LSHIFT;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
at_keyboard_status |= GRUB_TERM_STATUS_RSHIFT;
return 1;
case GRUB_KEYBOARD_KEY_LEFT_CTRL:
at_keyboard_status |= GRUB_TERM_STATUS_LCTRL;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
at_keyboard_status |= GRUB_TERM_STATUS_RCTRL;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_ALT:
at_keyboard_status |= GRUB_TERM_STATUS_RALT;
return 1;
case GRUB_KEYBOARD_KEY_LEFT_ALT:
at_keyboard_status |= GRUB_TERM_STATUS_LALT;
return 1;
default:
return 0;
}
else
switch (key)
{
case SHIFT_L:
at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_L;
break;
case SHIFT_R:
at_keyboard_status &= ~KEYBOARD_STATUS_SHIFT_R;
break;
case CTRL:
at_keyboard_status &= ~KEYBOARD_STATUS_CTRL_L;
break;
case ALT:
at_keyboard_status &= ~KEYBOARD_STATUS_ALT_L;
break;
default:
/* Skip grub_dprintf. */
return;
case GRUB_KEYBOARD_KEY_LEFT_SHIFT:
at_keyboard_status &= ~GRUB_TERM_STATUS_LSHIFT;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_SHIFT:
at_keyboard_status &= ~GRUB_TERM_STATUS_RSHIFT;
return 1;
case GRUB_KEYBOARD_KEY_LEFT_CTRL:
at_keyboard_status &= ~GRUB_TERM_STATUS_LCTRL;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_CTRL:
at_keyboard_status &= ~GRUB_TERM_STATUS_RCTRL;
return 1;
case GRUB_KEYBOARD_KEY_RIGHT_ALT:
at_keyboard_status &= ~GRUB_TERM_STATUS_RALT;
return 1;
case GRUB_KEYBOARD_KEY_LEFT_ALT:
at_keyboard_status &= ~GRUB_TERM_STATUS_LALT;
return 1;
default:
return 0;
}
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "Control key 0x%0x was %s\n", key, is_make ? "pressed" : "unpressed");
#endif
}
/* If there is a raw key pending, return it; otherwise return -1. */
static int
grub_keyboard_getkey (void)
{
grub_uint8_t key;
if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
int key;
int is_break;
key = fetch_key (&is_break);
if (key == -1)
return -1;
key = grub_inb (KEYBOARD_REG_DATA);
/* FIXME */ grub_keyboard_isr (key);
if (! KEYBOARD_ISMAKE (key))
if (grub_keyboard_isr (key, is_break))
return -1;
return (KEYBOARD_SCANCODE (key));
if (is_break)
return -1;
return key;
}
/* If there is a character pending, return it; otherwise return -1. */
/* If there is a character pending, return it;
otherwise return GRUB_TERM_NO_KEY. */
static int
grub_at_keyboard_getkey_noblock (void)
grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
{
int code, key;
int code;
code = grub_keyboard_getkey ();
if (code == -1)
return -1;
return GRUB_TERM_NO_KEY;
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "Detected key 0x%x\n", key);
#endif
switch (code)
{
case CAPS_LOCK:
at_keyboard_status ^= KEYBOARD_STATUS_CAPS_LOCK;
case GRUB_KEYBOARD_KEY_CAPS_LOCK:
at_keyboard_status ^= GRUB_TERM_STATUS_CAPS;
led_status ^= KEYBOARD_LED_CAPS;
keyboard_controller_led (led_status);
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "caps_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK));
#endif
key = -1;
break;
case NUM_LOCK:
at_keyboard_status ^= KEYBOARD_STATUS_NUM_LOCK;
return GRUB_TERM_NO_KEY;
case GRUB_KEYBOARD_KEY_NUM_LOCK:
at_keyboard_status ^= GRUB_TERM_STATUS_NUM;
led_status ^= KEYBOARD_LED_NUM;
keyboard_controller_led (led_status);
#ifdef DEBUG_AT_KEYBOARD
grub_dprintf ("atkeyb", "num_lock = %d\n", !!(at_keyboard_status & KEYBOARD_STATUS_NUM_LOCK));
#endif
key = -1;
break;
case SCROLL_LOCK:
/* For scroll lock we don't keep track of status. Only update its led. */
return GRUB_TERM_NO_KEY;
case GRUB_KEYBOARD_KEY_SCROLL_LOCK:
at_keyboard_status ^= GRUB_TERM_STATUS_SCROLL;
led_status ^= KEYBOARD_LED_SCROLL;
keyboard_controller_led (led_status);
key = -1;
break;
return GRUB_TERM_NO_KEY;
default:
if (at_keyboard_status & (KEYBOARD_STATUS_CTRL_L | KEYBOARD_STATUS_CTRL_R))
key = keyboard_map[code] - 'a' + 1;
else if ((at_keyboard_status & (KEYBOARD_STATUS_SHIFT_L | KEYBOARD_STATUS_SHIFT_R))
&& keyboard_map_shift[code])
key = keyboard_map_shift[code];
else
key = keyboard_map[code];
if (key == 0)
grub_dprintf ("atkeyb", "Unknown key 0x%x detected\n", code);
if (at_keyboard_status & KEYBOARD_STATUS_CAPS_LOCK)
{
if ((key >= 'a') && (key <= 'z'))
key += 'A' - 'a';
else if ((key >= 'A') && (key <= 'Z'))
key += 'a' - 'A';
}
return grub_term_map_key (code, at_keyboard_status);
}
return key;
}
static int
grub_at_keyboard_checkkey (struct grub_term_input *term __attribute__ ((unused)))
{
if (pending_key != -1)
return 1;
pending_key = grub_at_keyboard_getkey_noblock ();
if (pending_key != -1)
return 1;
return -1;
}
static int
grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
{
int key;
if (pending_key != -1)
{
key = pending_key;
pending_key = -1;
return key;
}
do
{
key = grub_at_keyboard_getkey_noblock ();
} while (key == -1);
return key;
}
static grub_err_t
grub_keyboard_controller_init (struct grub_term_input *term __attribute__ ((unused)))
{
pending_key = -1;
at_keyboard_status = 0;
/* Drain input buffer. */
while (1)
{
keyboard_controller_wait_until_ready ();
if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
break;
keyboard_controller_wait_until_ready ();
grub_inb (KEYBOARD_REG_DATA);
}
grub_keyboard_controller_orig = grub_keyboard_controller_read ();
grub_keyboard_controller_write (grub_keyboard_controller_orig | KEYBOARD_SCANCODE_SET1);
grub_keyboard_orig_set = query_mode ();
set_scancodes ();
keyboard_controller_led (led_status);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
{
if (grub_keyboard_orig_set)
write_mode (grub_keyboard_orig_set);
grub_keyboard_controller_write (grub_keyboard_controller_orig);
return GRUB_ERR_NONE;
}
static grub_err_t
grub_at_fini_hw (int noreturn __attribute__ ((unused)))
{
return grub_keyboard_controller_fini (NULL);
}
static grub_err_t
grub_at_restore_hw (void)
{
/* Drain input buffer. */
while (1)
{
keyboard_controller_wait_until_ready ();
if (! KEYBOARD_ISREADY (grub_inb (KEYBOARD_REG_STATUS)))
break;
keyboard_controller_wait_until_ready ();
grub_inb (KEYBOARD_REG_DATA);
}
set_scancodes ();
keyboard_controller_led (led_status);
return GRUB_ERR_NONE;
}
static struct grub_term_input grub_at_keyboard_term =
{
.name = "at_keyboard",
.init = grub_keyboard_controller_init,
.fini = grub_keyboard_controller_fini,
.checkkey = grub_at_keyboard_checkkey,
.getkey = grub_at_keyboard_getkey,
.getkey = grub_at_keyboard_getkey
};
GRUB_MOD_INIT(at_keyboard)
{
grub_term_register_input ("at_keyboard", &grub_at_keyboard_term);
grub_loader_register_preboot_hook (grub_at_fini_hw, grub_at_restore_hw,
GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
}
GRUB_MOD_FINI(at_keyboard)
{
grub_keyboard_controller_fini (NULL);
grub_term_unregister_input (&grub_at_keyboard_term);
}

View file

@ -28,8 +28,6 @@ static const grub_uint8_t
grub_console_standard_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW,
GRUB_EFI_BACKGROUND_BLACK);
static int read_key = -1;
static grub_uint32_t
map_char (grub_uint32_t c)
{
@ -103,8 +101,19 @@ grub_console_putchar (struct grub_term_output *term __attribute__ ((unused)),
efi_call_2 (o->output_string, o, str);
}
const unsigned efi_codes[] =
{
0, GRUB_TERM_UP, GRUB_TERM_DOWN, GRUB_TERM_RIGHT,
GRUB_TERM_LEFT, GRUB_TERM_HOME, GRUB_TERM_END, GRUB_TERM_KEY_INSERT,
GRUB_TERM_DC, GRUB_TERM_KEY_PPAGE, GRUB_TERM_KEY_NPAGE, GRUB_TERM_KEY_F1,
GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5,
GRUB_TERM_KEY_F6, GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9,
GRUB_TERM_KEY_F10, 0, 0, '\e'
};
static int
grub_console_checkkey (struct grub_term_input *term __attribute__ ((unused)))
grub_console_getkey (struct grub_term_input *term __attribute__ ((unused)))
{
grub_efi_simple_input_interface_t *i;
grub_efi_input_key_t key;
@ -113,129 +122,18 @@ grub_console_checkkey (struct grub_term_input *term __attribute__ ((unused)))
if (grub_efi_is_finished)
return 0;
if (read_key >= 0)
return 1;
i = grub_efi_system_table->con_in;
status = efi_call_2 (i->read_key_stroke, i, &key);
#if 0
switch (status)
{
case GRUB_EFI_SUCCESS:
{
grub_uint16_t xy;
xy = grub_getxy ();
grub_gotoxy (0, 0);
grub_printf ("scan_code=%x,unicode_char=%x ",
(unsigned) key.scan_code,
(unsigned) key.unicode_char);
grub_gotoxy (xy >> 8, xy & 0xff);
}
break;
if (status != GRUB_EFI_SUCCESS)
return GRUB_TERM_NO_KEY;
case GRUB_EFI_NOT_READY:
//grub_printf ("not ready ");
break;
if (key.scan_code == 0)
return key.unicode_char;
else if (key.scan_code < ARRAY_SIZE (efi_codes))
return efi_codes[key.scan_code];
default:
//grub_printf ("device error ");
break;
}
#endif
if (status == GRUB_EFI_SUCCESS)
{
switch (key.scan_code)
{
case 0x00:
read_key = key.unicode_char;
break;
case 0x01:
read_key = GRUB_TERM_UP;
break;
case 0x02:
read_key = GRUB_TERM_DOWN;
break;
case 0x03:
read_key = GRUB_TERM_RIGHT;
break;
case 0x04:
read_key = GRUB_TERM_LEFT;
break;
case 0x05:
read_key = GRUB_TERM_HOME;
break;
case 0x06:
read_key = GRUB_TERM_END;
break;
case 0x07:
break;
case 0x08:
read_key = GRUB_TERM_DC;
break;
case 0x09:
break;
case 0x0a:
break;
case 0x0b:
read_key = 24;
break;
case 0x0c:
read_key = 1;
break;
case 0x0d:
read_key = 5;
break;
case 0x0e:
read_key = 3;
break;
case 0x17:
read_key = '\e';
break;
default:
break;
}
}
return read_key;
}
static int
grub_console_getkey (struct grub_term_input *term)
{
grub_efi_simple_input_interface_t *i;
grub_efi_boot_services_t *b;
grub_efi_uintn_t index;
grub_efi_status_t status;
int key;
if (grub_efi_is_finished)
return 0;
if (read_key >= 0)
{
key = read_key;
read_key = -1;
return key;
}
i = grub_efi_system_table->con_in;
b = grub_efi_system_table->boot_services;
do
{
status = efi_call_3 (b->wait_for_event, 1, &(i->wait_for_key), &index);
if (status != GRUB_EFI_SUCCESS)
return -1;
grub_console_checkkey (term);
}
while (read_key < 0);
key = read_key;
read_key = -1;
return key;
return GRUB_TERM_NO_KEY;
}
static grub_uint16_t
@ -353,7 +251,6 @@ grub_efi_console_fini (struct grub_term_output *term)
static struct grub_term_input grub_console_term_input =
{
.name = "console",
.checkkey = grub_console_checkkey,
.getkey = grub_console_getkey,
};

View file

@ -24,33 +24,18 @@
static const struct grub_machine_bios_data_area *bios_data_area =
(struct grub_machine_bios_data_area *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
#define KEYBOARD_LEFT_SHIFT (1 << 0)
#define KEYBOARD_RIGHT_SHIFT (1 << 1)
#define KEYBOARD_CTRL (1 << 2)
#define KEYBOARD_ALT (1 << 3)
static int
grub_console_getkeystatus (struct grub_term_input *term __attribute__ ((unused)))
{
grub_uint8_t status = bios_data_area->keyboard_flag_lower;
int mods = 0;
if (status & (KEYBOARD_LEFT_SHIFT | KEYBOARD_RIGHT_SHIFT))
mods |= GRUB_TERM_STATUS_SHIFT;
if (status & KEYBOARD_CTRL)
mods |= GRUB_TERM_STATUS_CTRL;
if (status & KEYBOARD_ALT)
mods |= GRUB_TERM_STATUS_ALT;
return mods;
/* conveniently GRUB keystatus is modelled after BIOS one. */
return bios_data_area->keyboard_flag_lower & ~0x80;
}
static struct grub_term_input grub_console_term_input =
{
.name = "console",
.checkkey = grub_console_checkkey,
.getkey = grub_console_getkey,
.getkeystatus = grub_console_getkeystatus,
.getkeystatus = grub_console_getkeystatus
};
static struct grub_term_output grub_console_term_output =

View file

@ -194,7 +194,6 @@ static struct grub_term_input grub_ofconsole_term_input =
{
.name = "ofconsole",
.init = grub_ofconsole_init_input,
.checkkey = grub_terminfo_checkkey,
.getkey = grub_terminfo_getkey,
.data = &grub_ofconsole_terminfo_input
};

View file

@ -99,7 +99,6 @@ static struct grub_term_input grub_serial_term_input =
{
.name = "serial",
.init = grub_terminfo_input_init,
.checkkey = grub_terminfo_checkkey,
.getkey = grub_terminfo_getkey,
.data = &grub_serial_terminfo_input
};

View file

@ -403,34 +403,34 @@ grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
static struct
{
char key;
char ascii;
unsigned ascii;
}
three_code_table[] =
{
{'4', GRUB_TERM_DC},
{'A', GRUB_TERM_UP},
{'B', GRUB_TERM_DOWN},
{'C', GRUB_TERM_RIGHT},
{'D', GRUB_TERM_LEFT},
{'F', GRUB_TERM_END},
{'H', GRUB_TERM_HOME},
{'K', GRUB_TERM_END},
{'P', GRUB_TERM_DC},
{'?', GRUB_TERM_PPAGE},
{'/', GRUB_TERM_NPAGE}
{'4', GRUB_TERM_KEY_DC},
{'A', GRUB_TERM_KEY_UP},
{'B', GRUB_TERM_KEY_DOWN},
{'C', GRUB_TERM_KEY_RIGHT},
{'D', GRUB_TERM_KEY_LEFT},
{'F', GRUB_TERM_KEY_END},
{'H', GRUB_TERM_KEY_HOME},
{'K', GRUB_TERM_KEY_END},
{'P', GRUB_TERM_KEY_DC},
{'?', GRUB_TERM_KEY_PPAGE},
{'/', GRUB_TERM_KEY_NPAGE}
};
static struct
{
char key;
char ascii;
unsigned ascii;
}
four_code_table[] =
{
{'1', GRUB_TERM_HOME},
{'3', GRUB_TERM_DC},
{'5', GRUB_TERM_PPAGE},
{'6', GRUB_TERM_NPAGE}
{'1', GRUB_TERM_KEY_HOME},
{'3', GRUB_TERM_KEY_DC},
{'5', GRUB_TERM_KEY_PPAGE},
{'6', GRUB_TERM_KEY_NPAGE}
};
unsigned i;
@ -467,39 +467,30 @@ grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
#undef CONTINUE_READ
}
/* The terminfo version of checkkey. */
int
grub_terminfo_checkkey (struct grub_term_input *termi)
{
struct grub_terminfo_input_state *data
= (struct grub_terminfo_input_state *) (termi->data);
if (data->npending)
return data->input_buf[0];
grub_terminfo_readkey (termi, data->input_buf,
&data->npending, data->readkey);
if (data->npending)
return data->input_buf[0];
return -1;
}
/* The terminfo version of getkey. */
int
grub_terminfo_getkey (struct grub_term_input *termi)
{
struct grub_terminfo_input_state *data
= (struct grub_terminfo_input_state *) (termi->data);
int ret;
while (! data->npending)
grub_terminfo_readkey (termi, data->input_buf, &data->npending,
data->readkey);
if (data->npending)
{
data->npending--;
grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
return data->input_buf[0];
}
ret = data->input_buf[0];
data->npending--;
grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
return ret;
grub_terminfo_readkey (termi, data->input_buf,
&data->npending, data->readkey);
if (data->npending)
{
data->npending--;
grub_memmove (data->input_buf, data->input_buf + 1, data->npending);
return data->input_buf[0];
}
return GRUB_TERM_NO_KEY;
}
grub_err_t

View file

@ -25,36 +25,26 @@
#include <grub/usb.h>
#include <grub/dl.h>
#include <grub/time.h>
#include <grub/keyboard_layouts.h>
static char keyboard_map[128] =
enum
{
'\0', '\0', '\0', '\0', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '0',
'\n', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[',
']', '\\', '#', ';', '\'', '`', ',', '.',
'/', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', GRUB_TERM_HOME, GRUB_TERM_PPAGE, GRUB_TERM_DC, GRUB_TERM_END, GRUB_TERM_NPAGE, GRUB_TERM_RIGHT,
GRUB_TERM_LEFT, GRUB_TERM_DOWN, GRUB_TERM_UP
KEY_NO_KEY = 0x00,
KEY_ERR_BUFFER = 0x01,
KEY_ERR_POST = 0x02,
KEY_ERR_UNDEF = 0x03,
KEY_CAPS_LOCK = 0x39,
KEY_NUM_LOCK = 0x53,
};
static char keyboard_map_shift[128] =
enum
{
'\0', '\0', '\0', '\0', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
'#', '$', '%', '^', '&', '*', '(', ')',
'\n', '\0', '\0', '\0', ' ', '_', '+', '{',
'}', '|', '#', ':', '"', '`', '<', '>',
'?'
LED_NUM_LOCK = 0x01,
LED_CAPS_LOCK = 0x02
};
/* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
#define USB_HID_GET_REPORT 0x01
#define USB_HID_GET_IDLE 0x02
@ -66,28 +56,65 @@ static char keyboard_map_shift[128] =
#define USB_HID_BOOT_SUBCLASS 0x01
#define USB_HID_KBD_PROTOCOL 0x01
static int grub_usb_keyboard_checkkey (struct grub_term_input *term);
static int grub_usb_keyboard_getkey (struct grub_term_input *term);
static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
static struct grub_term_input grub_usb_keyboard_term =
{
.checkkey = grub_usb_keyboard_checkkey,
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
};
#define GRUB_USB_KEYBOARD_LEFT_CTRL 0x01
#define GRUB_USB_KEYBOARD_LEFT_SHIFT 0x02
#define GRUB_USB_KEYBOARD_LEFT_ALT 0x04
#define GRUB_USB_KEYBOARD_RIGHT_CTRL 0x10
#define GRUB_USB_KEYBOARD_RIGHT_SHIFT 0x20
#define GRUB_USB_KEYBOARD_RIGHT_ALT 0x40
struct grub_usb_keyboard_data
{
grub_usb_device_t usbdev;
grub_uint8_t status;
int key;
grub_uint16_t mods;
int interfno;
struct grub_usb_desc_endp *endp;
grub_usb_transfer_t transfer;
grub_uint8_t report[8];
int dead;
int last_key;
grub_uint64_t repeat_time;
grub_uint8_t current_report[8];
grub_uint8_t last_report[8];
int index;
int max_index;
};
static int grub_usb_keyboard_getkey (struct grub_term_input *term);
static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
static struct grub_term_input grub_usb_keyboard_term =
{
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
};
static struct grub_term_input grub_usb_keyboards[16];
static int
interpret_status (grub_uint8_t data0)
{
int mods = 0;
/* Check Shift, Control, and Alt status. */
if (data0 & GRUB_USB_KEYBOARD_LEFT_SHIFT)
mods |= GRUB_TERM_STATUS_LSHIFT;
if (data0 & GRUB_USB_KEYBOARD_RIGHT_SHIFT)
mods |= GRUB_TERM_STATUS_RSHIFT;
if (data0 & GRUB_USB_KEYBOARD_LEFT_CTRL)
mods |= GRUB_TERM_STATUS_LCTRL;
if (data0 & GRUB_USB_KEYBOARD_RIGHT_CTRL)
mods |= GRUB_TERM_STATUS_RCTRL;
if (data0 & GRUB_USB_KEYBOARD_LEFT_ALT)
mods |= GRUB_TERM_STATUS_LALT;
if (data0 & GRUB_USB_KEYBOARD_RIGHT_ALT)
mods |= GRUB_TERM_STATUS_RALT;
return mods;
}
static void
grub_usb_keyboard_detach (grub_usb_device_t usbdev,
int config __attribute__ ((unused)),
@ -104,6 +131,9 @@ grub_usb_keyboard_detach (grub_usb_device_t usbdev,
if (data->usbdev != usbdev)
continue;
if (data->transfer)
grub_usb_cancel_transfer (data->transfer);
grub_term_unregister_input (&grub_usb_keyboards[i]);
grub_free ((char *) grub_usb_keyboards[i].name);
grub_usb_keyboards[i].name = NULL;
@ -163,15 +193,19 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
}
data->usbdev = usbdev;
data->interfno = interfno;
data->endp = endp;
/* Configure device */
grub_usb_set_configuration (usbdev, configno + 1);
/* Place the device in boot mode. */
grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
USB_HID_SET_PROTOCOL, 0, 0, 0, 0);
USB_HID_SET_PROTOCOL, 0, interfno, 0, 0);
/* Reports every time an event occurs and not more often than that. */
grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
USB_HID_SET_IDLE, 0<<8, 0, 0, 0);
USB_HID_SET_IDLE, 0<<8, interfno, 0, 0);
grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
sizeof (grub_usb_keyboards[curnum]));
@ -185,24 +219,41 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
return 0;
}
/* Test showed that getting report may make the keyboard go nuts.
Moreover since we're reattaching keyboard it usually sends
an initial message on interrupt pipe and so we retrieve
the same keystatus.
*/
#if 0
{
grub_uint8_t report[8];
grub_usb_err_t err;
grub_memset (report, 0, sizeof (report));
err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
USB_HID_GET_REPORT, 0x0000, interfno,
USB_HID_GET_REPORT, 0x0100, interfno,
sizeof (report), (char *) report);
if (err)
{
data->status = 0;
data->key = -1;
}
data->status = 0;
else
{
data->status = report[0];
data->key = report[2] ? : -1;
}
data->status = report[0];
}
#else
data->status = 0;
#endif
data->transfer = grub_usb_bulk_read_background (usbdev,
data->endp->endp_addr,
sizeof (data->report),
(char *) data->report);
if (!data->transfer)
{
grub_print_error ();
return 0;
}
data->last_key = -1;
data->mods = 0;
data->dead = 0;
grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
@ -211,83 +262,176 @@ grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
static int
grub_usb_keyboard_checkkey (struct grub_term_input *term)
static void
send_leds (struct grub_usb_keyboard_data *termdata)
{
grub_uint8_t data[8];
grub_usb_err_t err;
struct grub_usb_keyboard_data *termdata = term->data;
grub_size_t actual;
if (termdata->key != -1)
return termdata->key;
data[2] = 0;
/* Poll interrupt pipe. */
err = grub_usb_bulk_read_extended (termdata->usbdev,
termdata->endp->endp_addr, sizeof (data),
(char *) data, 10, &actual);
if (err || actual < 1)
return -1;
termdata->status = data[0];
if (actual < 3 || !data[2])
return -1;
grub_dprintf ("usb_keyboard",
"report: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
/* Check if the Control or Shift key was pressed. */
if (data[0] & 0x01 || data[0] & 0x10)
termdata->key = keyboard_map[data[2]] - 'a' + 1;
else if (data[0] & 0x02 || data[0] & 0x20)
termdata->key = keyboard_map_shift[data[2]];
else
termdata->key = keyboard_map[data[2]];
if (termdata->key == 0)
grub_printf ("Unknown key 0x%x detected\n", data[2]);
char report[1];
report[0] = 0;
if (termdata->mods & GRUB_TERM_STATUS_CAPS)
report[0] |= LED_CAPS_LOCK;
if (termdata->mods & GRUB_TERM_STATUS_NUM)
report[0] |= LED_NUM_LOCK;
grub_usb_control_msg (termdata->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
USB_HID_SET_REPORT, 0x0200, termdata->interfno,
sizeof (report), (char *) report);
grub_errno = GRUB_ERR_NONE;
}
return termdata->key;
static int
parse_keycode (struct grub_usb_keyboard_data *termdata)
{
int index = termdata->index;
int i, keycode;
/* Sanity check */
if (index < 2)
index = 2;
for ( ; index < termdata->max_index; index++)
{
keycode = termdata->current_report[index];
if (keycode == KEY_NO_KEY
|| keycode == KEY_ERR_BUFFER
|| keycode == KEY_ERR_POST
|| keycode == KEY_ERR_UNDEF)
{
/* Don't parse (rest of) this report */
termdata->index = 0;
if (keycode != KEY_NO_KEY)
/* Don't replace last report with current faulty report
* in future ! */
grub_memcpy (termdata->current_report,
termdata->last_report,
sizeof (termdata->report));
return GRUB_TERM_NO_KEY;
}
/* Try to find current keycode in last report. */
for (i = 2; i < 8; i++)
if (keycode == termdata->last_report[i])
break;
if (i < 8)
/* Keycode is in last report, it means it was not released,
* ignore it. */
continue;
if (keycode == KEY_CAPS_LOCK)
{
termdata->mods ^= GRUB_TERM_STATUS_CAPS;
send_leds (termdata);
continue;
}
if (keycode == KEY_NUM_LOCK)
{
termdata->mods ^= GRUB_TERM_STATUS_NUM;
send_leds (termdata);
continue;
}
termdata->last_key = grub_term_map_key (keycode,
interpret_status (termdata->current_report[0])
| termdata->mods);
termdata->repeat_time = grub_get_time_ms () + GRUB_TERM_REPEAT_PRE_INTERVAL;
grub_errno = GRUB_ERR_NONE;
index++;
if (index >= termdata->max_index)
termdata->index = 0;
else
termdata->index = index;
return termdata->last_key;
}
/* All keycodes parsed */
termdata->index = 0;
return GRUB_TERM_NO_KEY;
}
static int
grub_usb_keyboard_getkey (struct grub_term_input *term)
{
int ret;
grub_usb_err_t err;
struct grub_usb_keyboard_data *termdata = term->data;
grub_size_t actual;
int keycode = GRUB_TERM_NO_KEY;
while (termdata->key == -1)
grub_usb_keyboard_checkkey (term);
if (termdata->dead)
return GRUB_TERM_NO_KEY;
ret = termdata->key;
if (termdata->index)
keycode = parse_keycode (termdata);
if (keycode != GRUB_TERM_NO_KEY)
return keycode;
/* Poll interrupt pipe. */
err = grub_usb_check_transfer (termdata->transfer, &actual);
termdata->key = -1;
if (err == GRUB_USB_ERR_WAIT)
{
if (termdata->last_key != -1
&& grub_get_time_ms () > termdata->repeat_time)
{
termdata->repeat_time = grub_get_time_ms ()
+ GRUB_TERM_REPEAT_INTERVAL;
return termdata->last_key;
}
return GRUB_TERM_NO_KEY;
}
return ret;
if (!err && (actual >= 3))
grub_memcpy (termdata->last_report,
termdata->current_report,
sizeof (termdata->report));
grub_memcpy (termdata->current_report,
termdata->report,
sizeof (termdata->report));
termdata->transfer = grub_usb_bulk_read_background (termdata->usbdev,
termdata->endp->endp_addr,
sizeof (termdata->report),
(char *) termdata->report);
if (!termdata->transfer)
{
grub_printf ("%s failed. Stopped\n", term->name);
termdata->dead = 1;
}
termdata->last_key = -1;
grub_dprintf ("usb_keyboard",
"err = %d, actual = %d report: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
err, actual,
termdata->current_report[0], termdata->current_report[1],
termdata->current_report[2], termdata->current_report[3],
termdata->current_report[4], termdata->current_report[5],
termdata->current_report[6], termdata->current_report[7]);
if (err || actual < 1)
return GRUB_TERM_NO_KEY;
termdata->status = termdata->current_report[0];
if (actual < 3)
return GRUB_TERM_NO_KEY;
termdata->index = 2; /* New data received. */
termdata->max_index = actual;
return parse_keycode (termdata);
}
static int
grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
{
struct grub_usb_keyboard_data *termdata = term->data;
int mods = 0;
/* Check Shift, Control, and Alt status. */
if (termdata->status & 0x02 || termdata->status & 0x20)
mods |= GRUB_TERM_STATUS_SHIFT;
if (termdata->status & 0x01 || termdata->status & 0x10)
mods |= GRUB_TERM_STATUS_CTRL;
if (termdata->status & 0x04 || termdata->status & 0x40)
mods |= GRUB_TERM_STATUS_ALT;
return mods;
return interpret_status (termdata->status) | termdata->mods;
}
struct grub_usb_attach_desc attach_hook =
@ -307,9 +451,18 @@ GRUB_MOD_FINI(usb_keyboard)
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
if (grub_usb_keyboards[i].data)
{
struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
if (!data)
continue;
if (data->transfer)
grub_usb_cancel_transfer (data->transfer);
grub_term_unregister_input (&grub_usb_keyboards[i]);
grub_free ((char *) grub_usb_keyboards[i].name);
grub_usb_keyboards[i].name = NULL;
grub_free (grub_usb_keyboards[i].data);
grub_usb_keyboards[i].data = 0;
}
grub_usb_unregister_attach_hook_class (&attach_hook);