Support USB keyboard auto-repeat.
authorKevin O'Connor <kevin@koconnor.net>
Fri, 12 Feb 2010 03:32:12 +0000 (22:32 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Fri, 12 Feb 2010 03:32:12 +0000 (22:32 -0500)
Support handling of multiple keys pressed simultanously.
Support auto-repeat via USB HID Set_Idle command.
Also, add "noinline" directives to reduce stack usage of timer irq.

src/biosvar.h
src/mouse.c
src/usb-hid.c
src/usb.c

index 2e43f8b122e4b1558725c4a0e1ded1115b5143d0..d011966b132adfe369bda60de56ae3326d0a87a3 100644 (file)
@@ -215,6 +215,17 @@ struct fdpt_s {
     u8 checksum;
 } PACKED;
 
+struct usbkeyinfo {
+    union {
+        struct {
+            u8 modifiers;
+            u8 repeatcount;
+            u8 keys[6];
+        };
+        u64 data;
+    };
+};
+
 struct extended_bios_data_area_s {
     u8 size;
     u8 reserved1[0x21];
@@ -232,6 +243,8 @@ struct extended_bios_data_area_s {
     u8 other2[0xC4];
 
     // 0x121 - Begin custom storage.
+    struct usbkeyinfo usbkey_last;
+
     int RTCusers;
 
     // El Torito Emulation data
index 888d32debf57ad7f1bf594bb39ae3df7bae494fa..3004d7864aef5da7e016c3142e7a9a29764096dc 100644 (file)
@@ -269,7 +269,7 @@ handle_15c2(struct bregs *regs)
     }
 }
 
-void
+void noinline
 process_mouse(u8 data)
 {
     if (!CONFIG_MOUSE)
index 7756c620faf85d10041260ef955221bc76e49772..b714685f26514d8544c55f47dae541ed3d1e666b 100644 (file)
@@ -30,17 +30,20 @@ set_protocol(u32 endp, u16 val)
 }
 
 static int
-set_idle(u32 endp, u8 val)
+set_idle(u32 endp, int ms)
 {
     struct usb_ctrlrequest req;
     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
     req.bRequest = HID_REQ_SET_IDLE;
-    req.wValue = val<<8;
+    req.wValue = (ms/4)<<8;
     req.wIndex = 0;
     req.wLength = 0;
     return send_default_control(endp, &req, NULL);
 }
 
+#define KEYREPEATWAITMS 500
+#define KEYREPEATMS 33
+
 int
 usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
 {
@@ -75,7 +78,7 @@ usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
     if (ret)
         return -1;
     // Only send reports on a new key event.
-    ret = set_idle(endp, 0);
+    ret = set_idle(endp, KEYREPEATMS);
     if (ret)
         return -1;
 
@@ -121,6 +124,8 @@ static u16 ModifierToScanCode[] VAR16 = {
     0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
 };
 
+#define RELEASEBIT 0x80
+
 struct keyevent {
     u8 modifiers;
     u8 reserved;
@@ -135,8 +140,8 @@ prockeys(u16 keys)
         if (key == 0xe1) {
             // Pause key
             process_key(0xe1);
-            process_key(0x1d | (keys & 0x80));
-            process_key(0x45 | (keys & 0x80));
+            process_key(0x1d | (keys & RELEASEBIT));
+            process_key(0x45 | (keys & RELEASEBIT));
             return;
         }
         process_key(key);
@@ -145,26 +150,89 @@ prockeys(u16 keys)
 }
 
 static void
+procscankey(u8 key, u8 flags)
+{
+    if (key >= ARRAY_SIZE(KeyToScanCode))
+        return;
+    u16 keys = GET_GLOBAL(KeyToScanCode[key]);
+    if (keys)
+        prockeys(keys | flags);
+}
+
+static void
+procmodkey(u8 mods, u8 flags)
+{
+    int i;
+    for (i=0; mods; i++)
+        if (mods & (1<<i)) {
+            // Modifier key change.
+            prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags);
+            mods &= ~(1<<i);
+        }
+}
+
+static void noinline
 handle_key(struct keyevent *data)
 {
     dprintf(5, "Got key %x %x\n", data->modifiers, data->keys[0]);
-    // XXX
+
+    // Load old keys.
+    u16 ebda_seg = get_ebda_seg();
+    struct usbkeyinfo old;
+    old.data = GET_EBDA2(ebda_seg, usbkey_last.data);
+
+    // Check for keys no longer pressed.
+    int addpos = 0;
     int i;
-    for (i=0; i<8; i++)
-        if (data->modifiers & (1<<i))
-            prockeys(GET_GLOBAL(ModifierToScanCode[i]));
+    for (i=0; i<ARRAY_SIZE(old.keys); i++) {
+        u8 key = old.keys[i];
+        if (!key)
+            break;
+        int j;
+        for (j=0;; j++) {
+            if (j>=ARRAY_SIZE(data->keys)) {
+                // Key released.
+                procscankey(key, RELEASEBIT);
+                if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1])
+                    // Last pressed key released - disable repeat.
+                    old.repeatcount = 0xff;
+                break;
+            }
+            if (data->keys[j] == key) {
+                // Key still pressed.
+                data->keys[j] = 0;
+                old.keys[addpos++] = key;
+                break;
+            }
+        }
+    }
+    procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT);
+
+    // Process new keys
+    procmodkey(data->modifiers & ~old.modifiers, 0);
+    old.modifiers = data->modifiers;
     for (i=0; i<ARRAY_SIZE(data->keys); i++) {
         u8 key = data->keys[i];
-        if (key >= ARRAY_SIZE(KeyToScanCode))
-            continue;
-        key = GET_GLOBAL(KeyToScanCode[key]);
         if (!key)
             continue;
-        prockeys(key);
+        // New key pressed.
+        procscankey(key, 0);
+        old.keys[addpos++] = key;
+        old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1;
     }
-    for (i=0; i<8; i++)
-        if (data->modifiers & (1<<i))
-            prockeys(GET_GLOBAL(ModifierToScanCode[i]) | 0x80);
+    if (addpos < ARRAY_SIZE(old.keys))
+        old.keys[addpos] = 0;
+
+    // Check for key repeat event.
+    if (addpos) {
+        if (!old.repeatcount)
+            procscankey(old.keys[addpos-1], 0);
+        else if (old.repeatcount != 0xff)
+            old.repeatcount--;
+    }
+
+    // Update old keys
+    SET_EBDA2(ebda_seg, usbkey_last.data, old.data);
 }
 
 void
index 2bd5832f6d12dca2b6fcaa03c187d16cfd91a59f..12747dbc3dc253dff66f198fad5c7bbfae83704f 100644 (file)
--- a/src/usb.c
+++ b/src/usb.c
@@ -44,7 +44,7 @@ alloc_intr_pipe(u32 endp, int period)
     }
 }
 
-int
+int noinline
 usb_poll_intr(struct usb_pipe *pipe, void *data)
 {
     u32 endp = GET_FLATPTR(pipe->endp);