diff --git a/ChangeLog b/ChangeLog index 0686af0a2..0ec24e970 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2009-02-21 Robert Millan + + Implement USB keyboard support (based on patch by Marco Gerards) + + * conf/i386-pc.rmk (pkglib_MODULES): Add `usb_keyboard.mod'. + (usb_keyboard_mod_SOURCES, usb_keyboard_mod_CFLAGS) + (usb_keyboard_mod_LDFLAGS): New variables. + + * term/usb_keyboard.c: New file. + 2009-02-14 Vladimir Serbinenko Corrected wrong declaration diff --git a/DISTLIST b/DISTLIST index 63cff3dd6..a92f73323 100644 --- a/DISTLIST +++ b/DISTLIST @@ -425,6 +425,7 @@ partmap/sun.c term/gfxterm.c term/terminfo.c term/tparm.c +term/usb_keyboard.c term/efi/console.c term/i386/vga_common.c term/i386/pc/at_keyboard.c diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index ee7dad8f1..72e0957e5 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -980,7 +980,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \ ata.mod vga.mod memdisk.mod pci.mod lspci.mod \ aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ - usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod + usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod # For biosdisk.mod. biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c @@ -3054,6 +3054,63 @@ partmap-usbms_mod-disk_usbms.lst: disk/usbms.c $(disk/usbms.c_DEPENDENCIES) genp usbms_mod_CFLAGS = $(COMMON_CFLAGS) usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For usb_keyboard.mod +usb_keyboard_mod_SOURCES = term/usb_keyboard.c +CLEANFILES += usb_keyboard.mod mod-usb_keyboard.o mod-usb_keyboard.c pre-usb_keyboard.o usb_keyboard_mod-term_usb_keyboard.o und-usb_keyboard.lst +ifneq ($(usb_keyboard_mod_EXPORTS),no) +CLEANFILES += def-usb_keyboard.lst +DEFSYMFILES += def-usb_keyboard.lst +endif +MOSTLYCLEANFILES += usb_keyboard_mod-term_usb_keyboard.d +UNDSYMFILES += und-usb_keyboard.lst + +usb_keyboard.mod: pre-usb_keyboard.o mod-usb_keyboard.o $(TARGET_OBJ2ELF) + -rm -f $@ + $(TARGET_CC) $(usb_keyboard_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-usb_keyboard.o mod-usb_keyboard.o + if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ + +pre-usb_keyboard.o: $(usb_keyboard_mod_DEPENDENCIES) usb_keyboard_mod-term_usb_keyboard.o + -rm -f $@ + $(TARGET_CC) $(usb_keyboard_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ usb_keyboard_mod-term_usb_keyboard.o + +mod-usb_keyboard.o: mod-usb_keyboard.c + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(usb_keyboard_mod_CFLAGS) -c -o $@ $< + +mod-usb_keyboard.c: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'usb_keyboard' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(usb_keyboard_mod_EXPORTS),no) +def-usb_keyboard.lst: pre-usb_keyboard.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 usb_keyboard/' > $@ +endif + +und-usb_keyboard.lst: pre-usb_keyboard.o + echo 'usb_keyboard' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +usb_keyboard_mod-term_usb_keyboard.o: term/usb_keyboard.c $(term/usb_keyboard.c_DEPENDENCIES) + $(TARGET_CC) -Iterm -I$(srcdir)/term $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(usb_keyboard_mod_CFLAGS) -MD -c -o $@ $< +-include usb_keyboard_mod-term_usb_keyboard.d + +CLEANFILES += cmd-usb_keyboard_mod-term_usb_keyboard.lst fs-usb_keyboard_mod-term_usb_keyboard.lst partmap-usb_keyboard_mod-term_usb_keyboard.lst +COMMANDFILES += cmd-usb_keyboard_mod-term_usb_keyboard.lst +FSFILES += fs-usb_keyboard_mod-term_usb_keyboard.lst +PARTMAPFILES += partmap-usb_keyboard_mod-term_usb_keyboard.lst + +cmd-usb_keyboard_mod-term_usb_keyboard.lst: term/usb_keyboard.c $(term/usb_keyboard.c_DEPENDENCIES) gencmdlist.sh + set -e; $(TARGET_CC) -Iterm -I$(srcdir)/term $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(usb_keyboard_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh usb_keyboard > $@ || (rm -f $@; exit 1) + +fs-usb_keyboard_mod-term_usb_keyboard.lst: term/usb_keyboard.c $(term/usb_keyboard.c_DEPENDENCIES) genfslist.sh + set -e; $(TARGET_CC) -Iterm -I$(srcdir)/term $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(usb_keyboard_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh usb_keyboard > $@ || (rm -f $@; exit 1) + +partmap-usb_keyboard_mod-term_usb_keyboard.lst: term/usb_keyboard.c $(term/usb_keyboard.c_DEPENDENCIES) genpartmaplist.sh + set -e; $(TARGET_CC) -Iterm -I$(srcdir)/term $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(usb_keyboard_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh usb_keyboard > $@ || (rm -f $@; exit 1) + + +usb_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) +usb_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For pxe.mod pxe_mod_SOURCES = fs/i386/pc/pxe.c CLEANFILES += pxe.mod mod-pxe.o mod-pxe.c pre-pxe.o pxe_mod-fs_i386_pc_pxe.o und-pxe.lst diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index b8f1c6d8a..fd66ce9fc 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -173,7 +173,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \ ata.mod vga.mod memdisk.mod pci.mod lspci.mod \ aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ - usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod + usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod # For biosdisk.mod. biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c @@ -335,6 +335,11 @@ usbms_mod_SOURCES = disk/usbms.c usbms_mod_CFLAGS = $(COMMON_CFLAGS) usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For usb_keyboard.mod +usb_keyboard_mod_SOURCES = term/usb_keyboard.c +usb_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) +usb_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For pxe.mod pxe_mod_SOURCES = fs/i386/pc/pxe.c pxe_mod_CFLAGS = $(COMMON_CFLAGS) diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c new file mode 100644 index 000000000..07f7d08b4 --- /dev/null +++ b/term/usb_keyboard.c @@ -0,0 +1,257 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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; + +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, 0x21, 0x0B, 0, 0, 0, 0); + + /* Reports everytime an event occurs and not more often than that. */ + grub_usb_control_msg (usbdev, 0x21, 0x0A, 0<<8, 0, 0, 0); +} + +static grub_err_t +grub_usb_keyboard_getreport (grub_usb_device_t dev, unsigned char *report) +{ + return grub_usb_control_msg (dev, (1 << 7) | (1 << 5) | 1, 0x01, 0, 0, + 8, (char *) report); +} + + + +static int +grub_usb_keyboard_checkkey (void) +{ + unsigned char data[8]; + int key; + int i; + grub_err_t err; + + data[2] = 0; + for (i = 0; i < 50; i++) + { + /* Get_Report. */ + err = grub_usb_keyboard_getreport (usbdev, data); + + if (! err && data[2]) + break; + } + + 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, (1 << 7) | (1 << 5) | 1, 0x01, 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; + int key_release; + grub_err_t err; + unsigned char 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_NONE: + timeout = 100; + break; + case GRUB_HIDBOOT_REPEAT_FIRST: + timeout = 500; + break; + case GRUB_HIDBOOT_REPEAT: + timeout = 50; + 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 struct grub_term_input grub_usb_keyboard_term = + { + .name = "usb_keyboard", + .checkkey = grub_usb_keyboard_checkkey, + .getkey = grub_usb_keyboard_getkey, + .next = 0 + }; + +GRUB_MOD_INIT(usb_keyboard) +{ + (void) mod; /* To stop warning. */ + + grub_usb_hid (); + grub_term_register_input (&grub_usb_keyboard_term); +} + +GRUB_MOD_FINI(usb_keyboard) +{ + grub_term_unregister_input (&grub_usb_keyboard_term); +}