From: Colin Watson <cjwatson@ubuntu.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Subject: Re: [PATCH] Detect key modifier status in 'sleep --interruptible'
Date: Wed, 26 Aug 2009 16:33:56 +0100 [thread overview]
Message-ID: <20090826153356.GA6830@riva.ucam.org> (raw)
In-Reply-To: <20090824230217.GM11691@riva.ucam.org>
On Tue, Aug 25, 2009 at 12:02:17AM +0100, Colin Watson wrote:
> I'm open to other ideas. 'sleep --interruptible 0' is not an entirely
> natural way to express what I'm looking for, so I'm not particularly
> attached to it. 'if sleep --interruptible 0; then set timeout=0; fi' is
> suboptimal because it means that if either (a) core.img is too old to
> have getkeystatus support or (b) the current console driver does not
> support getkeystatus then the menu will be skipped without a way to
> access it at run-time. I'd much rather have something that allowed
> zero-timeout-with-Shift-to-access-menu if and only if we have a new
> enough core.img and the console driver has getkeystatus support, and
> otherwise degraded to something else sensible.
Here's yet another version, following a discussion with Robert on IRC. I
think that this now satisfies Vladimir's concerns about not breaking
platforms that don't have a usable getkeystatus, and my concerns about
backward compatibility with old core.img files. The new keystatus
command is, I think, also a rather clearer way to express this.
I expect to use this something like this:
set timeout=10
if keystatus; then
if keystatus --shift; then
true
else
set timeout=0
fi
fi
I left part of the sleep change in for some degree of consistency, but
removed the change to the case where the sleep period is zero. I also
adjusted the USB keyboard driver to tweak the idle rate temporarily to
make sure that we get events even if a modifier isn't pressed or
released during the timeout.
2009-08-24 Colin Watson <cjwatson@ubuntu.com>
2009-08-24 Robert Millan <rmh.grub@aybabtu.com>
Add `getkeystatus' terminal method. Use it in `sleep' to detect
Shift being held down. 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_keyboard_getkeystatus): New
function.
(grub_usb_keyboard_term): Set `getkeystatus' member.
* commands/sleep.c (grub_check_keyboard): New function.
(grub_interruptible_millisleep): Use grub_check_keyboard,
checking whether Shift is pressed in addition to checking for
Escape.
* 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.
Index: conf/common.rmk
===================================================================
--- conf/common.rmk (revision 2535)
+++ conf/common.rmk (working copy)
@@ -363,7 +363,8 @@
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
@@ -510,6 +511,11 @@
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 \
Index: conf/i386-pc.rmk
===================================================================
--- conf/i386-pc.rmk (revision 2535)
+++ conf/i386-pc.rmk (working copy)
@@ -123,7 +123,7 @@
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 \
\
Index: conf/i386-efi.rmk
===================================================================
--- conf/i386-efi.rmk (revision 2535)
+++ conf/i386-efi.rmk (working copy)
@@ -37,7 +37,7 @@
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 \
Index: conf/i386-ieee1275.rmk
===================================================================
--- conf/i386-ieee1275.rmk (revision 2535)
+++ conf/i386-ieee1275.rmk (working copy)
@@ -66,7 +66,7 @@
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 \
Index: conf/x86_64-efi.rmk
===================================================================
--- conf/x86_64-efi.rmk (revision 2535)
+++ conf/x86_64-efi.rmk (working copy)
@@ -37,7 +37,7 @@
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 \
Index: conf/i386-coreboot.rmk
===================================================================
--- conf/i386-coreboot.rmk (revision 2535)
+++ conf/i386-coreboot.rmk (working copy)
@@ -110,7 +110,7 @@
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 \
\
Index: conf/powerpc-ieee1275.rmk
===================================================================
--- conf/powerpc-ieee1275.rmk (revision 2535)
+++ conf/powerpc-ieee1275.rmk (working copy)
@@ -46,7 +46,7 @@
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 \
Index: conf/sparc64-ieee1275.rmk
===================================================================
--- conf/sparc64-ieee1275.rmk (revision 2535)
+++ conf/sparc64-ieee1275.rmk (working copy)
@@ -103,7 +103,7 @@
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 \
Index: kern/term.c
===================================================================
--- kern/term.c (revision 2535)
+++ kern/term.c (working copy)
@@ -140,6 +140,15 @@
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)
{
Index: include/grub/term.h
===================================================================
--- include/grub/term.h (revision 2535)
+++ include/grub/term.h (working copy)
@@ -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 @@
#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 @@
/* 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 @@
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);
Index: include/grub/i386/pc/memory.h
===================================================================
--- include/grub/i386/pc/memory.h (revision 2535)
+++ include/grub/i386/pc/memory.h (working copy)
@@ -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;
Index: commands/sleep.c
===================================================================
--- commands/sleep.c (revision 2535)
+++ commands/sleep.c (working copy)
@@ -1,7 +1,7 @@
/* sleep.c - Command to wait a specified number of seconds. */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
+ * Copyright (C) 2006,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
@@ -43,6 +43,20 @@
grub_printf ("%d ", n);
}
+static int
+grub_check_keyboard (void)
+{
+ int mods = grub_getkeystatus ();
+ if (mods >= 0 && (mods & GRUB_TERM_STATUS_SHIFT) != 0)
+ return 1;
+
+ if (grub_checkkey () >= 0 &&
+ GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC)
+ return 1;
+
+ return 0;
+}
+
/* Based on grub_millisleep() from kern/generic/millisleep.c. */
static int
grub_interruptible_millisleep (grub_uint32_t ms)
@@ -52,8 +66,7 @@
start = grub_get_time_ms ();
while (grub_get_time_ms () - start < ms)
- if (grub_checkkey () >= 0 &&
- GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC)
+ if (grub_check_keyboard ())
return 1;
return 0;
Index: commands/keystatus.c
===================================================================
--- commands/keystatus.c (revision 0)
+++ commands/keystatus.c (revision 0)
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/term.h>
+
+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;
+\f
+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);
+}
Index: term/i386/pc/console.c
===================================================================
--- term/i386/pc/console.c (revision 2535)
+++ term/i386/pc/console.c (working copy)
@@ -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,35 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <grub/machine/memory.h>
#include <grub/machine/console.h>
#include <grub/term.h>
#include <grub/types.h>
+static const struct grub_machine_bios_data_area *bios_data_area = GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
+
+static int
+grub_console_getkeystatus (void)
+{
+ grub_uint8_t status = bios_data_area->keyboard_flag_lower;
+ int mods = 0;
+
+ if (status & 0x03)
+ mods |= GRUB_TERM_STATUS_SHIFT;
+ if (status & 0x04)
+ mods |= GRUB_TERM_STATUS_CTRL;
+ if (status & 0x08)
+ 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 =
Index: term/usb_keyboard.c
===================================================================
--- term/usb_keyboard.c (revision 2535)
+++ term/usb_keyboard.c (working copy)
@@ -238,11 +238,67 @@
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, 0x21, 0x0A, 1<<8, 0, 0, 0);
+
+ grub_usb_control_msg (usbdev, (1 << 7) | (1 << 5) | 1, 0x02, 0, 0, 1, (char *) data);
+
+ 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, 0x21, 0x0A, 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
};
--
Colin Watson [cjwatson@chiark.greenend.org.uk]
next prev parent reply other threads:[~2009-08-26 15:34 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-12 15:35 [PATCH] Detect key modifier status in 'sleep --interruptible' Colin Watson
2009-08-12 16:10 ` Vladimir 'phcoder' Serbinenko
2009-08-12 16:15 ` Vladimir 'phcoder' Serbinenko
2009-08-12 22:14 ` Colin Watson
2009-08-13 20:52 ` Robert Millan
2009-08-13 21:15 ` Colin Watson
2009-08-23 22:46 ` Vladimir 'phcoder' Serbinenko
2009-08-23 22:56 ` Robert Millan
2009-08-23 23:00 ` Vladimir 'phcoder' Serbinenko
2009-08-23 23:03 ` Robert Millan
2009-08-23 23:05 ` Vladimir 'phcoder' Serbinenko
2009-08-25 19:26 ` Robert Millan
2009-08-24 9:24 ` Colin Watson
2009-08-24 12:23 ` Robert Millan
2009-08-24 12:44 ` Robert Millan
2009-08-24 13:27 ` Colin Watson
2009-08-24 14:28 ` Robert Millan
2009-08-24 16:23 ` Colin Watson
2009-08-24 16:56 ` Colin Watson
2009-08-24 17:03 ` Colin Watson
2009-08-24 22:04 ` Colin Watson
2009-08-24 22:41 ` Robert Millan
2009-08-24 23:02 ` Colin Watson
2009-08-26 15:33 ` Colin Watson [this message]
2009-08-26 18:39 ` Robert Millan
2009-08-26 20:26 ` Colin Watson
2009-08-28 12:44 ` Robert Millan
2009-08-28 13:20 ` Colin Watson
2009-08-24 12:56 ` Robert Millan
2009-08-13 20:46 ` Robert Millan
2009-08-23 22:27 ` Robert Millan
2009-08-23 22:41 ` Vladimir 'phcoder' Serbinenko
2009-08-23 22:57 ` Robert Millan
2009-08-24 9:11 ` Colin Watson
2009-08-24 11:57 ` Robert Millan
2009-08-24 12:50 ` Vladimir 'phcoder' Serbinenko
2009-08-24 12:58 ` Michal Suchanek
2009-08-24 14:29 ` Robert Millan
2009-08-24 12:59 ` Colin Watson
2009-08-24 13:27 ` Robert Millan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090826153356.GA6830@riva.ucam.org \
--to=cjwatson@ubuntu.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.