Hi, at_keyboard's checkkey() implementation is buggy. It will flush the controller's input buffer, but checkkey()'s semantics require that it's not flushed. This results in some keys being lost when using at_keyboard.
A proper fix that adjusts to this API is complicated. I implemented one [1], only to realize afterwards that maybe this mess isn't really necessary. A quick check at grub_checkkey() users reveals that none of them care about the actual key, even if grub_checkkey returns it. They just want to know whether there's a key available for read at all. If we removed this constraint from the checkkey() semantics, perhaps at_keyboard can get a checkkey() with less hassle. However, when at_keyboard reads an event, it needs to determine whether it's a MAKE (key hit) or BREAK (key unhit) event. Which AFAICT can't be done without reading KEYBOARD_REG_DATA. But maybe I'm missing something. Any bright ideas? [1] See attachment. This made me implement queues as well, perhaps some of this can be reused. -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all."
Index: conf/i386.rmk =================================================================== --- conf/i386.rmk (revision 2607) +++ conf/i386.rmk (working copy) @@ -6,7 +6,7 @@ cpuid_mod_CFLAGS = $(COMMON_CFLAGS) cpuid_mod_LDFLAGS = $(COMMON_LDFLAGS) pkglib_MODULES += at_keyboard.mod -at_keyboard_mod_SOURCES = term/i386/pc/at_keyboard.c +at_keyboard_mod_SOURCES = term/i386/pc/at_keyboard.c kern/queue.c at_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) at_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) Index: kern/queue.c =================================================================== --- kern/queue.c (revision 0) +++ kern/queue.c (revision 0) @@ -0,0 +1,53 @@ +/* queue.c - grub queue functions */ +/* + * 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/queue.h> +#include <grub/misc.h> +#include <grub/mm.h> + +void +grub_queue_push (grub_queue_t queue, grub_queue_item_t item) +{ + item->next = queue->head; + item->prev = NULL; + if (queue->head) + queue->head->prev = item; + queue->head = item; + + if (! queue->tail) + /* Empty queue becomes non-empty. */ + queue->tail = item; +} + +void * +grub_queue_pop (grub_queue_t queue) +{ + grub_queue_item_t item; + + item = queue->tail; + if (queue->tail) + queue->tail = item->prev; + if (queue->tail) + queue->tail->next = NULL; + else + /* Non-empty queue becomes empty. */ + queue->head = NULL; + + return item; +} Index: include/grub/queue.h =================================================================== --- include/grub/queue.h (revision 0) +++ include/grub/queue.h (revision 0) @@ -0,0 +1,52 @@ +/* queue.h - header for grub queue */ +/* + * 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/>. + */ + +#ifndef GRUB_QUEUE_HEADER +#define GRUB_QUEUE_HEADER 1 + +#include <grub/symbol.h> +#include <grub/types.h> +#include <grub/list.h> /* GRUB_FIELD_MATCH */ + +struct grub_queue_item +{ + struct grub_queue_item *next, *prev; +}; +typedef struct grub_queue_item *grub_queue_item_t; + +struct grub_queue +{ + grub_queue_item_t head, tail; +}; +typedef struct grub_queue *grub_queue_t; + +void EXPORT_FUNC(grub_queue_push) (grub_queue_t queue, grub_queue_item_t item); +void * EXPORT_FUNC(grub_queue_pop) (grub_queue_t queue); + +extern void* grub_assert_fail (void); + +#define GRUB_AS_QUEUE_ITEM(ptr) \ + (GRUB_FIELD_MATCH (ptr, grub_queue_item_t, next) && GRUB_FIELD_MATCH (ptr, grub_queue_item_t, prev) ? \ + (grub_queue_item_t) ptr : grub_assert_fail ()) + +#define GRUB_AS_QUEUE_ITEM_P(pptr) \ + (GRUB_FIELD_MATCH (*pptr, grub_queue_item_t, next) && GRUB_FIELD_MATCH (*pptr, grub_queue_item_t, prev) ? \ + (grub_queue_item_t *) (void *) pptr : grub_assert_fail ()) + +#endif /* ! GRUB_QUEUE_HEADER */ Index: term/i386/pc/at_keyboard.c =================================================================== --- term/i386/pc/at_keyboard.c (revision 2607) +++ term/i386/pc/at_keyboard.c (working copy) @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007,2008 Free Software Foundation, Inc. + * Copyright (C) 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 @@ -22,7 +22,52 @@ #include <grub/i386/io.h> #include <grub/misc.h> #include <grub/term.h> +#include <grub/mm.h> +#include <grub/queue.h> +struct grub_at_keyboard_queue_item +{ + struct grub_at_keyboard_queue_item *next, *prev; + int key; +}; +typedef struct grub_at_keyboard_queue_item *grub_at_keyboard_queue_item_t; + +static struct grub_queue grub_at_keyboard_queue; + +static void +grub_at_keyboard_queue_push (int key) +{ + grub_at_keyboard_queue_item_t item; + item = grub_malloc (sizeof (*item)); + item->key = key; + grub_queue_push (&grub_at_keyboard_queue, GRUB_AS_QUEUE_ITEM (item)); +} + +static int +grub_at_keyboard_queue_pop (void) +{ + grub_at_keyboard_queue_item_t item; + int key; + item = grub_queue_pop (&grub_at_keyboard_queue); + if (item) + { + key = item->key; + grub_free (item); + } + else + key = -1; + return key; +} + +static int +grub_at_keyboard_queue_last (void) +{ + if (grub_at_keyboard_queue.tail) + return ((grub_at_keyboard_queue_item_t) grub_at_keyboard_queue.tail)->key; + else + return -1; +} + static short at_keyboard_status = 0; #define KEYBOARD_STATUS_SHIFT_L (1 << 0) @@ -146,7 +191,7 @@ grub_keyboard_getkey (void) /* If there is a character pending, return it; otherwise return -1. */ static int -grub_at_keyboard_checkkey (void) +grub_at_keyboard_getkey_noblock (void) { int code, key; code = grub_keyboard_getkey (); @@ -186,10 +231,24 @@ static int key += 'a' - 'A'; } } - return (int) key; + return key; } static int +grub_at_keyboard_checkkey (void) +{ + int key; + key = grub_at_keyboard_getkey_noblock (); + + /* If there was a key, queue it. */ + if (key != -1) + grub_at_keyboard_queue_push (key); + + /* Return last key from queue. */ + return grub_at_keyboard_queue_last (); +} + +static int grub_at_keyboard_getkey (void) { int key; @@ -197,6 +256,10 @@ grub_at_keyboard_getkey (void) { key = grub_at_keyboard_checkkey (); } while (key == -1); + + /* Remove it from the queue. */ + (void) grub_at_keyboard_queue_pop (); + return key; }
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel