From 4cbe67e509788b3aaa19a57afa2184c8d9f1061c Mon Sep 17 00:00:00 2001 From: cjwatson Date: Fri, 28 Aug 2009 13:20:34 +0000 Subject: [PATCH] 2009-08-28 Colin Watson 2009-08-28 Robert Millan Add `getkeystatus' terminal method. Add a new `keystatus' command to query it. * include/grub/term.h (GRUB_TERM_STATUS_SHIFT, GRUB_TERM_STATUS_CTRL, GRUB_TERM_STATUS_ALT): Definitions for modifier key bitmasks. (struct grub_term_input): Add `getkeystatus' member. (grub_getkeystatus): Add prototype. * kern/term.c (grub_getkeystatus): New function. * include/grub/i386/pc/memory.h (GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR): New macro. (struct grub_machine_bios_data_area): Define necessary parts of BIOS Data Area layout. * term/i386/pc/console.c (grub_console_getkeystatus): New function. (grub_console_term_input): Set `getkeystatus' member. * term/usb_keyboard.c (grub_usb_hid): Macroify HID protocol constants. (grub_usb_keyboard_getreport): Likewise. (grub_usb_keyboard_checkkey): Likewise. (grub_usb_keyboard_getkeystatus): New function. (grub_usb_keyboard_term): Set `getkeystatus' member. * commands/keystatus.c: New file. * conf/common.rmk (pkglib_MODULES): Add keystatus.mod. (keystatus_mod_SOURCES): New variable. (keystatus_mod_CFLAGS): Likewise. (keystatus_mod_LDFLAGS): Likewise. * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add commands/keystatus.c. * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. * DISTLIST: Add commands/keystatus.c. --- ChangeLog | 41 ++++++++++++++++++ commands/keystatus.c | 81 ++++++++++++++++++++++++++++++++++ conf/common.rmk | 8 +++- conf/i386-coreboot.rmk | 2 +- conf/i386-efi.rmk | 2 +- conf/i386-ieee1275.rmk | 2 +- conf/i386-pc.rmk | 2 +- conf/powerpc-ieee1275.rmk | 2 +- conf/sparc64-ieee1275.rmk | 2 +- conf/x86_64-efi.rmk | 2 +- include/grub/i386/pc/memory.h | 11 +++++ include/grub/term.h | 12 ++++- kern/term.c | 9 ++++ term/i386/pc/console.c | 27 +++++++++++- term/usb_keyboard.c | 82 ++++++++++++++++++++++++++++++++--- 15 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 commands/keystatus.c diff --git a/ChangeLog b/ChangeLog index ab542e2e8..691273fc6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2009-08-28 Colin Watson +2009-08-28 Robert Millan + + Add `getkeystatus' terminal method. Add a new `keystatus' command + to query it. + + * include/grub/term.h (GRUB_TERM_STATUS_SHIFT, + GRUB_TERM_STATUS_CTRL, GRUB_TERM_STATUS_ALT): Definitions for + modifier key bitmasks. + (struct grub_term_input): Add `getkeystatus' member. + (grub_getkeystatus): Add prototype. + * kern/term.c (grub_getkeystatus): New function. + + * include/grub/i386/pc/memory.h + (GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR): New macro. + (struct grub_machine_bios_data_area): Define necessary parts of BIOS + Data Area layout. + * term/i386/pc/console.c (grub_console_getkeystatus): New function. + (grub_console_term_input): Set `getkeystatus' member. + * term/usb_keyboard.c (grub_usb_hid): Macroify HID protocol + constants. + (grub_usb_keyboard_getreport): Likewise. + (grub_usb_keyboard_checkkey): Likewise. + (grub_usb_keyboard_getkeystatus): New function. + (grub_usb_keyboard_term): Set `getkeystatus' member. + + * commands/keystatus.c: New file. + * conf/common.rmk (pkglib_MODULES): Add keystatus.mod. + (keystatus_mod_SOURCES): New variable. + (keystatus_mod_CFLAGS): Likewise. + (keystatus_mod_LDFLAGS): Likewise. + * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add + commands/keystatus.c. + * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. + * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. + * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. + * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. + * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. + * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. + * DISTLIST: Add commands/keystatus.c. + 2009-08-28 Vladimir Serbinenko Split befs.mod and afs.mod into *_be.mod and *.mod diff --git a/commands/keystatus.c b/commands/keystatus.c new file mode 100644 index 000000000..cecbba504 --- /dev/null +++ b/commands/keystatus.c @@ -0,0 +1,81 @@ +/* keystatus.c - Command to check key modifier status. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 + +static const struct grub_arg_option options[] = + { + {"shift", 's', 0, "check Shift key", 0, 0}, + {"ctrl", 'c', 0, "check Control key", 0, 0}, + {"alt", 'a', 0, "check Alt key", 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +#define grub_cur_term_input grub_term_get_current_input () + +static grub_err_t +grub_cmd_keystatus (grub_extcmd_t cmd, + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_arg_list *state = cmd->state; + int expect_mods = 0; + int mods; + + if (state[0].set) + expect_mods |= GRUB_TERM_STATUS_SHIFT; + if (state[1].set) + expect_mods |= GRUB_TERM_STATUS_CTRL; + if (state[2].set) + expect_mods |= GRUB_TERM_STATUS_ALT; + + /* Without arguments, just check whether getkeystatus is supported at + all. */ + if (!grub_cur_term_input->getkeystatus) + return grub_error (GRUB_ERR_TEST_FAILURE, "false"); + grub_dprintf ("keystatus", "expect_mods: %d\n", expect_mods); + if (!expect_mods) + return 0; + + mods = grub_getkeystatus (); + grub_dprintf ("keystatus", "mods: %d\n", mods); + if (mods >= 0 && (mods & expect_mods) != 0) + return 0; + else + return grub_error (GRUB_ERR_TEST_FAILURE, "false"); +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(keystatus) +{ + cmd = grub_register_extcmd ("keystatus", grub_cmd_keystatus, + GRUB_COMMAND_FLAG_BOTH, + "keystatus [--shift|--ctrl|--alt]", + "Check key modifier status", + options); +} + +GRUB_MOD_FINI(keystatus) +{ + grub_unregister_extcmd (cmd); +} diff --git a/conf/common.rmk b/conf/common.rmk index 7727f19e8..73cf02caa 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -375,7 +375,8 @@ pkglib_MODULES += minicmd.mod extcmd.mod hello.mod handler.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ read.mod sleep.mod loadenv.mod crc.mod parttool.mod \ msdospart.mod memrw.mod normal.mod sh.mod lua.mod \ - gptsync.mod true.mod probe.mod password.mod + gptsync.mod true.mod probe.mod password.mod \ + keystatus.mod # For password.mod. password_mod_SOURCES = commands/password.c @@ -522,6 +523,11 @@ probe_mod_SOURCES = commands/probe.c probe_mod_CFLAGS = $(COMMON_CFLAGS) probe_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For keystatus.mod. +keystatus_mod_SOURCES = commands/keystatus.c +keystatus_mod_CFLAGS = $(COMMON_CFLAGS) +keystatus_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For normal.mod. normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \ normal/auth.c normal/autofs.c normal/handler.c \ diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 990ba805d..4e2d1f1ec 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -110,7 +110,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/handler.c commands/ls.c commands/test.c \ commands/search.c commands/blocklist.c commands/hexdump.c \ commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ lib/hexdump.c commands/i386/cpuid.c \ disk/host.c disk/loopback.c \ \ diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index b470a7687..18a9a636b 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -37,7 +37,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/configfile.c commands/help.c \ commands/handler.c commands/ls.c commands/test.c \ commands/search.c commands/hexdump.c lib/hexdump.c \ - commands/halt.c commands/reboot.c \ + commands/halt.c commands/reboot.c commands/keystatus.c \ commands/i386/cpuid.c \ commands/password.c \ disk/loopback.c \ diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index 989f1bc61..b426bdd80 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -66,7 +66,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ lib/hexdump.c commands/halt.c commands/reboot.c \ commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ commands/i386/cpuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ disk/host.c disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index d415aed32..8d7d9fca9 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -124,7 +124,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ lib/hexdump.c commands/i386/pc/halt.c commands/reboot.c \ commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ commands/i386/cpuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ disk/host.c disk/loopback.c disk/scsi.c \ fs/fshelp.c \ \ diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 0c6650dfd..b294de917 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -46,7 +46,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/ls.c commands/blocklist.c commands/hexdump.c \ lib/hexdump.c commands/halt.c commands/reboot.c \ commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/conf/sparc64-ieee1275.rmk b/conf/sparc64-ieee1275.rmk index 7851fd64a..c44c6ca78 100644 --- a/conf/sparc64-ieee1275.rmk +++ b/conf/sparc64-ieee1275.rmk @@ -104,7 +104,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/ls.c commands/blocklist.c commands/hexdump.c \ lib/hexdump.c commands/halt.c commands/reboot.c \ commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 0defdcd93..1a4cf89ed 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -37,7 +37,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/search.c commands/hexdump.c lib/hexdump.c \ commands/halt.c commands/reboot.c \ commands/i386/cpuid.c \ - commands/password.c \ + commands/password.c commands/keystatus.c \ disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/include/grub/i386/pc/memory.h b/include/grub/i386/pc/memory.h index d7910ab15..815390482 100644 --- a/include/grub/i386/pc/memory.h +++ b/include/grub/i386/pc/memory.h @@ -78,8 +78,19 @@ /* The data segment of the pseudo real mode. */ #define GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG 0x20 +#define GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR 0x400 + #ifndef ASM_FILE +/* See http://heim.ifi.uio.no/~stanisls/helppc/bios_data_area.html for a + description of the BIOS Data Area layout. */ +struct grub_machine_bios_data_area +{ + grub_uint8_t unused1[0x17]; + grub_uint8_t keyboard_flag_lower; /* 0x17 */ + grub_uint8_t unused2[0xf0 - 0x18]; +}; + struct grub_machine_mmap_entry { grub_uint32_t size; diff --git a/include/grub/term.h b/include/grub/term.h index 9ce3be7d1..316d75390 100644 --- a/include/grub/term.h +++ b/include/grub/term.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2005,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2005,2007,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 @@ -72,6 +72,12 @@ grub_term_color_state; #define GRUB_TERM_NEED_INIT (1 << 16) +/* Bitmasks for modifier keys returned by grub_getkeystatus. */ +#define GRUB_TERM_STATUS_SHIFT (1 << 0) +#define GRUB_TERM_STATUS_CTRL (1 << 1) +#define GRUB_TERM_STATUS_ALT (1 << 2) + + /* Unicode characters for fancy graphics. */ #define GRUB_TERM_DISP_LEFT 0x2190 #define GRUB_TERM_DISP_UP 0x2191 @@ -157,6 +163,9 @@ struct grub_term_input /* Get a character. */ int (*getkey) (void); + + /* Get keyboard modifier status. */ + int (*getkeystatus) (void); }; typedef struct grub_term_input *grub_term_input_t; @@ -275,6 +284,7 @@ void EXPORT_FUNC(grub_putcode) (grub_uint32_t code); grub_ssize_t EXPORT_FUNC(grub_getcharwidth) (grub_uint32_t code); int EXPORT_FUNC(grub_getkey) (void); int EXPORT_FUNC(grub_checkkey) (void); +int EXPORT_FUNC(grub_getkeystatus) (void); grub_uint16_t EXPORT_FUNC(grub_getwh) (void); grub_uint16_t EXPORT_FUNC(grub_getxy) (void); void EXPORT_FUNC(grub_gotoxy) (grub_uint8_t x, grub_uint8_t y); diff --git a/kern/term.c b/kern/term.c index 3583a301d..03e2ae573 100644 --- a/kern/term.c +++ b/kern/term.c @@ -140,6 +140,15 @@ grub_checkkey (void) return (grub_cur_term_input->checkkey) (); } +int +grub_getkeystatus (void) +{ + if (grub_cur_term_input->getkeystatus) + return (grub_cur_term_input->getkeystatus) (); + else + return 0; +} + grub_uint16_t grub_getxy (void) { diff --git a/term/i386/pc/console.c b/term/i386/pc/console.c index c880595e4..ec1383942 100644 --- a/term/i386/pc/console.c +++ b/term/i386/pc/console.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2005,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2002,2003,2005,2007,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 @@ -16,15 +16,40 @@ * along with GRUB. If not, see . */ +#include #include #include #include +static const struct grub_machine_bios_data_area *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 (void) +{ + 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; +} + static struct grub_term_input grub_console_term_input = { .name = "console", .checkkey = grub_console_checkkey, .getkey = grub_console_getkey, + .getkeystatus = grub_console_getkeystatus, }; static struct grub_term_output grub_console_term_output = diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index bb62b35ed..7f5e058f7 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -58,6 +58,19 @@ static char keyboard_map_shift[128] = 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 0x61 + +/* 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) { @@ -90,17 +103,19 @@ grub_usb_hid (void) grub_usb_iterate (usb_iterate); /* Place the device in boot mode. */ - grub_usb_control_msg (usbdev, 0x21, 0x0B, 0, 0, 0, 0); + 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, 0x21, 0x0A, 0<<8, 0, 0, 0); + 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, (1 << 7) | (1 << 5) | 1, 0x01, 0, 0, - 8, (char *) report); + return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT, + 0, 0, 8, (char *) report); } @@ -151,7 +166,8 @@ grub_usb_keyboard_checkkey (void) /* Wait until the key is released. */ while (!err && data[2]) { - err = grub_usb_control_msg (usbdev, (1 << 7) | (1 << 5) | 1, 0x01, 0, 0, + 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" @@ -238,11 +254,67 @@ grub_usb_keyboard_getkey (void) 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 };