grub/term/usb_keyboard.c
Robert Millan f84b481b35 Cheery-pick grub-emu split from experimental (rev 1361)
2009-11-23  Robert Millan  <rmh.grub@aybabtu.com>

        * configure.ac: Detect all `emu' platforms.  Define
        GRUB_MACHINE_* macros in TARGET_CFLAGS.  Remove
        --enable-grub-emu logic.  Disable include/grub/machine
        symlink on `emu' platforms.

        * genkernsyms.sh.in: Use @TARGET_CFLAGS@ during symbol generation.
        * gensymlist.sh.in: Likewise.

        * include/grub/i386/coreboot/machine.h: Remove file.
        * include/grub/i386/efi/machine.h: Likewise.
        * include/grub/i386/ieee1275/machine.h: Likewise.
        * include/grub/i386/pc/machine.h: Likewise.
        * include/grub/i386/qemu/machine.h: Likewise.
        * include/grub/powerpc/ieee1275/machine.h: Likewise.
        * include/grub/sparc64/ieee1275/machine.h: Likewise.
        * include/grub/x86_64/efi/machine.h: Likewise.

        * commands/acpi.c: Remove `<grub/machine/machine.h>'.
        * commands/halt.c: Likewise.
        * commands/reboot.c: Likewise.
        * include/grub/autoefi.h: Likewise.
        * include/grub/i386/at_keyboard.h: Likewise.
        * include/grub/i386/kernel.h: Likewise.
        * include/grub/i386/loader.h: Likewise.
        * include/grub/i386/pc/memory.h: Likewise.
        * kern/dl.c: Likewise.
        * kern/i386/coreboot/init.c: Likewise.
        * loader/i386/bsd.c: Likewise.
        * loader/i386/linux.c: Likewise.
        * loader/multiboot_loader.c: Likewise.
        * term/i386/pc/serial.c: Likewise.
        * term/usb_keyboard.c: Likewise.

        * include/grub/time.h [!GRUB_MACHINE_EMU]: Remove
        `<grub/machine/machine.h>'
        [!GRUB_MACHINE_EMU] (GRUB_TICKS_PER_SECOND): New macro.
        * util/misc.c: Remove `<grub/machine/machine.h>' and
        `<grub/machine/time.h>'.

        * Makefile.in (enable_grub_emu): Remove variable.
        Include $(srcdir)/conf/any-emu.mk for the `emu' platform.

        * conf/any-emu.rmk: New file.
        * conf/common.rmk (grub_emu_init.lst, grub_emu_init.h)
        (grub_emu_init.c): Move from here ...
        * conf/any-emu.rmk: ... to here.

        * conf/i386-coreboot.rmk (sbin_UTILITIES): Remove `grub-emu'.
        (grub_emu_SOURCES, grub_emu_LDFLAGS): Move from here ...
        * conf/any-emu.rmk: ... to here.
2009-11-23 15:31:54 +00:00

329 lines
8 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Support for the HID Boot Protocol. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008, 2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/term.h>
#include <grub/machine/console.h>
#include <grub/time.h>
#include <grub/cpu/io.h>
#include <grub/misc.h>
#include <grub/term.h>
#include <grub/usb.h>
#include <grub/dl.h>
#include <grub/time.h>
static char keyboard_map[128] =
{
'\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
};
static char keyboard_map_shift[128] =
{
'\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', ' ', '_', '+', '{',
'}', '|', '#', ':', '"', '`', '<', '>',
'?'
};
static grub_usb_device_t usbdev;
/* Valid values for bmRequestType. See HID definition version 1.11 section
7.2. */
#define USB_HID_HOST_TO_DEVICE 0x21
#define USB_HID_DEVICE_TO_HOST 0xA1
/* 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
#define USB_HID_GET_PROTOCOL 0x03
#define USB_HID_SET_REPORT 0x09
#define USB_HID_SET_IDLE 0x0A
#define USB_HID_SET_PROTOCOL 0x0B
static void
grub_usb_hid (void)
{
struct grub_usb_desc_device *descdev;
auto int usb_iterate (grub_usb_device_t dev);
int usb_iterate (grub_usb_device_t dev)
{
descdev = &dev->descdev;
grub_dprintf ("usb_keyboard", "%x %x %x\n",
descdev->class, descdev->subclass, descdev->protocol);
#if 0
if (descdev->class != 0x09
|| descdev->subclass == 0x01
|| descdev->protocol != 0x02)
return 0;
#endif
if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
return 0;
grub_printf ("HID found!\n");
usbdev = dev;
return 1;
}
grub_usb_iterate (usb_iterate);
/* Place the device in boot mode. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
0, 0, 0, 0);
/* Reports every time an event occurs and not more often than that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
}
static grub_err_t
grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
{
return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
0, 0, 8, (char *) report);
}
static int
grub_usb_keyboard_checkkey (void)
{
grub_uint8_t data[8];
int key;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
data[2] = 0;
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[2]);
if (err || !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)
key = keyboard_map[data[2]] - 'a' + 1;
else if (data[0] & 0x02 || data[0] & 0x20)
key = keyboard_map_shift[data[2]];
else
key = keyboard_map[data[2]];
if (key == 0)
grub_printf ("Unknown key 0x%x detected\n", data[2]);
#if 0
/* Wait until the key is released. */
while (!err && data[2])
{
err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST,
USB_HID_GET_REPORT, 0, 0,
sizeof (data), (char *) data);
grub_dprintf ("usb_keyboard",
"report2: 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]);
}
#endif
grub_errno = GRUB_ERR_NONE;
return key;
}
typedef enum
{
GRUB_HIDBOOT_REPEAT_NONE,
GRUB_HIDBOOT_REPEAT_FIRST,
GRUB_HIDBOOT_REPEAT
} grub_usb_keyboard_repeat_t;
static int
grub_usb_keyboard_getkey (void)
{
int key;
grub_err_t err;
grub_uint8_t data[8];
grub_uint64_t currtime;
int timeout;
static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
again:
do
{
key = grub_usb_keyboard_checkkey ();
} while (key == -1);
data[2] = !0; /* Or whatever. */
err = 0;
switch (repeat)
{
case GRUB_HIDBOOT_REPEAT_FIRST:
timeout = 500;
break;
case GRUB_HIDBOOT_REPEAT:
timeout = 50;
break;
default:
timeout = 100;
break;
}
/* Wait until the key is released. */
currtime = grub_get_time_ms ();
while (!err && data[2])
{
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
{
if (repeat == 0)
repeat = 1;
else
repeat = 2;
grub_errno = GRUB_ERR_NONE;
return key;
}
err = grub_usb_keyboard_getreport (usbdev, data);
}
if (repeat)
{
repeat = 0;
goto again;
}
repeat = 0;
grub_errno = GRUB_ERR_NONE;
return key;
}
static int
grub_usb_keyboard_getkeystatus (void)
{
grub_uint8_t data[8];
int mods = 0;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
/* Set idle time to the minimum offered by the spec (4 milliseconds) so
that we can find out the current state. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[0]);
/* Go back to reporting every time an event occurs and not more often than
that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
/* We allowed a while for modifiers to show up in the report, but it is
not an error if they never did. */
if (err)
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 Shift, Control, and Alt status. */
if (data[0] & 0x02 || data[0] & 0x20)
mods |= GRUB_TERM_STATUS_SHIFT;
if (data[0] & 0x01 || data[0] & 0x10)
mods |= GRUB_TERM_STATUS_CTRL;
if (data[0] & 0x04 || data[0] & 0x40)
mods |= GRUB_TERM_STATUS_ALT;
grub_errno = GRUB_ERR_NONE;
return mods;
}
static struct grub_term_input grub_usb_keyboard_term =
{
.name = "usb_keyboard",
.checkkey = grub_usb_keyboard_checkkey,
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
};
GRUB_MOD_INIT(usb_keyboard)
{
grub_usb_hid ();
grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
}
GRUB_MOD_FINI(usb_keyboard)
{
grub_term_unregister_input (&grub_usb_keyboard_term);
}