[PATCH] 2.3.99 - Keyboard soft autorepeat

From: Russell King (rmk@arm.linux.org.uk)
Date: Sun Mar 26 2000 - 10:12:31 EST


Hi,

Here is the keyboard autorepeat patch. It should apply to 2.3.99pre* kernels.

I'm not going to say it's SMP safe - it probably isn't. Maybe you guys can
help get it into a state where its reasonable to go into the mainsteam kernel.

The following patch:

1. Cleans up unnecessary initialisers.
2. Cleans up some formatting.
3. Adds keyboard soft-autorepeat (but retains the original behaviour for
   Xfree via the VC RAW keyboard mode).
4. Adds an extra function - kbd_reset_down() so that low level keyboard
   drivers can inform this layer that the key down arrays should be cleared.

The keyboard rate can be altered by the "DEFAULT_REPEAT_*" macros, however
please note that they are set rather high at the moment.

Todo:

1. Set sensible defaults.
2. Make repeat settings run-time configurable.

If anyone would like to fix either of the above two points, please contact
me.

I'm CC'ing linux-kernel for comment/other interest only. I do not intend
this patch to go into the Linux kernel at this time. New versions of this
can usually be found contained within the ARM patches at:

        ftp.arm.linux.org.uk:/pub/armlinux/source/kernel-patches

If you wish to use a different version, you will have to dig out the relevent
hunks from those patches, but you're on your own there.

diff -urN linux-orig/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux-orig/drivers/char/keyboard.c Fri Feb 18 20:52:54 2000
+++ linux/drivers/char/keyboard.c Fri Feb 18 21:05:20 2000
@@ -21,6 +21,10 @@
  *
  * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
  * 30-07-98: Dead keys redone, aeb@cwi.nl.
+ *
+ * 04-04-1998: Added keyboard autorepeat support (some keyboards don't
+ * autorepeat, and some keyboard changers interfere with keyboard
+ * autorepeat settings). - Russell King (rmk@arm.uk.linux.org)
  */
 
 #include <linux/config.h>
@@ -30,6 +34,7 @@
 #include <linux/tty_flip.h>
 #include <linux/mm.h>
 #include <linux/string.h>
+#include <linux/timer.h>
 #include <linux/random.h>
 #include <linux/init.h>
 
@@ -61,6 +66,14 @@
 #define KBD_DEFLOCK 0
 #endif
 
+/*
+ * Default autorepeat settings.
+ * DEFAULT_REPEAT_TIMEOUT is the timeout from the keypress to the first repeat
+ * DEFAULT_REPEAT_INTERVAL is the timeout between successive repeats
+ */
+#define DEFAULT_REPEAT_TIMEOUT HZ*300/1000
+#define DEFAULT_REPEAT_INTERVAL HZ*30/1000
+
 void (*kbd_ledfunc)(unsigned int led) = NULL;
 EXPORT_SYMBOL(handle_scancode);
 EXPORT_SYMBOL(kbd_ledfunc);
@@ -83,25 +96,29 @@
  */
 
 /* shift state counters.. */
-static unsigned char k_down[NR_SHIFT] = {0, };
+static unsigned char k_down[NR_SHIFT];
 /* keyboard key bitmap */
-static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
+static unsigned long key_down[256/BITS_PER_LONG];
 
-static int dead_key_next = 0;
+static int dead_key_next;
 /*
  * In order to retrieve the shift_state (for the mouse server), either
  * the variable must be global, or a new procedure must be created to
  * return the value. I chose the former way.
  */
-int shift_state = 0;
+int shift_state;
 static int npadch = -1; /* -1 or number assembled on pad */
-static unsigned char diacr = 0;
-static char rep = 0; /* flag telling character repeat */
+static unsigned char diacr;
+static char rep; /* flag telling character repeat */
+static int kbd_repeatkeycode= -1;
+static int kbd_repeattimeout = DEFAULT_REPEAT_TIMEOUT;
+static int kbd_repeatinterval= DEFAULT_REPEAT_INTERVAL;
 struct kbd_struct kbd_table[MAX_NR_CONSOLES];
 static struct tty_struct **ttytab;
 static struct kbd_struct * kbd = kbd_table;
-static struct tty_struct * tty = NULL;
+static struct tty_struct * tty;
 
+static void kbd_processkeycode(unsigned char scancode, char up_flag, int autorepeat);
 void compute_shiftstate(void);
 
 typedef void (*k_hand)(unsigned char value, char up_flag);
@@ -169,7 +186,8 @@
  * string, and in both cases we might assume that it is
  * in utf-8 already.
  */
-void to_utf8(ushort c) {
+void to_utf8(ushort c)
+{
     if (c < 0x80)
         put_queue(c); /* 0******* */
     else if (c < 0x800) {
@@ -188,17 +206,23 @@
  * Translation of escaped scancodes to keycodes.
  * This is now user-settable (for machines were it makes sense).
  */
-
 int setkeycode(unsigned int scancode, unsigned int keycode)
 {
- return kbd_setkeycode(scancode, keycode);
+ return kbd_setkeycode(scancode, keycode);
 }
 
 int getkeycode(unsigned int scancode)
 {
- return kbd_getkeycode(scancode);
+ return kbd_getkeycode(scancode);
 }
 
+static void key_callback(unsigned long nr);
+
+static struct timer_list key_autorepeat_timer =
+{
+ NULL, NULL, 0, 0, key_callback
+};
+
 void handle_scancode(unsigned char scancode, int down)
 {
         unsigned char keycode;
@@ -207,14 +231,12 @@
 
         pm_access(pm_kbd);
 
- do_poke_blanked_console = 1;
- tasklet_schedule(&console_tasklet);
         add_keyboard_randomness(scancode | up_flag);
 
         tty = ttytab? ttytab[fg_console]: NULL;
         if (tty && (!tty->driver_data)) {
                 /*
- * We touch the tty structure via the the ttytab array
+ * We touch the tty structure via the ttytab array
                  * without knowing whether or not tty is open, which
                  * is inherently dangerous. We currently rely on that
                  * fact that console_open sets tty->driver_data when
@@ -243,12 +265,33 @@
          * return the keycode if in MEDIUMRAW mode.
          */
 
+ kbd_processkeycode(keycode, up_flag, 0);
+}
+
+static void
+kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)
+{
+ char raw_mode = (kbd->kbdmode == VC_RAW);
+
         if (up_flag) {
                 rep = 0;
                 if(!test_and_clear_bit(keycode, key_down))
                     up_flag = kbd_unexpected_up(keycode);
- } else
+ } else {
                 rep = test_and_set_bit(keycode, key_down);
+ /* If the keyboard autorepeated for us, ignore it.
+ * We do our own autorepeat processing.
+ */
+ if (rep && !autorepeat)
+ return;
+ }
+
+ if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {
+ kbd_repeatkeycode = -1;
+ del_timer(&key_autorepeat_timer);
+ }
+ do_poke_blanked_console = 1;
+ tasklet_schedule(&console_tasklet);
 
 #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
         if (keycode == SYSRQ_KEY) {
@@ -262,6 +305,23 @@
         }
 #endif
 
+ /*
+ * Calculate the next time when we have to do some autorepeat
+ * processing. Note that we do not do autorepeat processing
+ * while in raw mode but we do do autorepeat processing in
+ * medium raw mode.
+ */
+ if (!up_flag && !raw_mode) {
+ kbd_repeatkeycode = keycode;
+ if (vc_kbd_mode(kbd, VC_REPEAT)) {
+ if (rep)
+ key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;
+ else
+ key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;
+ add_timer(&key_autorepeat_timer);
+ }
+ }
+
         if (kbd->kbdmode == VC_MEDIUMRAW) {
                 /* soon keycodes will require more than one byte */
                 put_queue(keycode + up_flag);
@@ -330,6 +390,23 @@
 #endif
                 }
         }
+ rep = 0;
+}
+
+/*
+ * This clears the key down arrays when the keyboard is reset. On
+ * keyboard reset, this must be called before any keycodes are
+ * received.
+ */
+void kbd_reset_kdown(void)
+{
+ int i;
+
+ for (i = 0; i < NR_SHIFT; i++)
+ k_down[i] = 0;
+ for (i = 0; i < SIZE(key_down); i++)
+ key_down[i] = 0;
+ shift_state = 0;
 }
 
 
@@ -518,7 +595,7 @@
 {
 }
 
-static void do_null()
+static void do_null(void)
 {
         compute_shiftstate();
 }
@@ -633,8 +710,8 @@
 
 static void do_pad(unsigned char value, char up_flag)
 {
- static const char *pad_chars = "0123456789+-*/\015,.?()";
- static const char *app_map = "pqrstuvwxylSRQMnnmPQ";
+ static const char *pad_chars = "0123456789+-*/\015,.?()#";
+ static const char *app_map = "pqrstuvwxylSRQMnnmPQS";
 
         if (up_flag)
                 return; /* no action, if this is a key release */
@@ -735,9 +812,10 @@
         }
 }
 
-/* called after returning from RAW mode or when changing consoles -
- recompute k_down[] and shift_state from key_down[] */
-/* maybe called when keymap is undefined, so that shiftkey release is seen */
+/* Called after returning from RAW mode or when changing consoles -
+ * recompute k_down[] and shift_state from key_down[]
+ * Maybe called when keymap is undefined so that shift key release is seen
+ */
 void compute_shiftstate(void)
 {
         int i, j, k, sym, val;
@@ -816,19 +894,22 @@
 }
 
 /*
- * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
- * or (ii) whatever pattern of lights people want to show using KDSETLED,
- * or (iii) specified bits of specified words in kernel memory.
+ * The leds display either
+ * (i) the status of NumLock, CapsLock, ScrollLock, or
+ * (ii) whatever pattern of lights people want to show using KDSETLED, or
+ * (iii) specified bits of specified words in kernel memory.
  */
 
 static unsigned char ledstate = 0xff; /* undefined */
 static unsigned char ledioctl;
 
-unsigned char getledstate(void) {
+unsigned char getledstate(void)
+{
     return ledstate;
 }
 
-void setledstate(struct kbd_struct *kbd, unsigned int led) {
+void setledstate(struct kbd_struct *kbd, unsigned int led)
+{
     if (!(led & ~7)) {
         ledioctl = led;
         kbd->ledmode = LED_SHOW_IOCTL;
@@ -844,7 +925,8 @@
 } ledptrs[3];
 
 void register_leds(int console, unsigned int led,
- unsigned int *addr, unsigned int mask) {
+ unsigned int *addr, unsigned int mask)
+{
     struct kbd_struct *kbd = kbd_table + console;
     if (led < 3) {
         ledptrs[led].addr = addr;
@@ -855,7 +937,8 @@
         kbd->ledmode = LED_SHOW_FLAGS;
 }
 
-static inline unsigned char getleds(void){
+static inline unsigned char getleds(void)
+{
     struct kbd_struct *kbd = kbd_table + fg_console;
     unsigned char leds;
 
@@ -902,6 +985,19 @@
 {
         unsigned char leds = getleds();
 
+ if (rep && kbd_repeatkeycode != -1) {
+ tty = ttytab? ttytab[fg_console]: NULL;
+ kbd = kbd_table + fg_console;
+
+ /* This prevents the kbd_key routine from being called
+ * twice, once by this BH, and once by the interrupt
+ * routine.
+ */
+ kbd_disable_irq();
+ kbd_processkeycode(kbd_repeatkeycode, 0, 1);
+ kbd_enable_irq();
+ }
+
         if (leds != ledstate) {
                 ledstate = leds;
                 kbd_leds(leds);
@@ -912,6 +1008,12 @@
 EXPORT_SYMBOL(keyboard_tasklet);
 DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
 
+static void key_callback(unsigned long nr)
+{
+ rep = 1;
+ tasklet_schedule(&keyboard_tasklet);
+}
+
 int __init kbd_init(void)
 {
         int i;
@@ -934,7 +1036,7 @@
 
         tasklet_enable(&keyboard_tasklet);
         tasklet_schedule(&keyboard_tasklet);
-
+
         pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, NULL);
 
         return 0;

   _____
  |_____| ------------------------------------------------- ---+---+-
  | | Russell King rmk@arm.linux.org.uk --- ---
  | | | | http://www.arm.linux.org.uk/~rmk/aboutme.html / / |
  | +-+-+ --- -+-
  / | THE developer of ARM Linux |+| /|\
 / | | | --- |
    +-+-+ ------------------------------------------------- /\\\ |

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Mar 31 2000 - 21:00:17 EST