This patch adds auto-repeat support for USB keyboards. It also fixes handling of events where multiple keys are pressed simultaneously (which can happen when typing fast).
The repeat is accomplished by using the keyboard Set_Idle command. Unfortunately, qemu doesn't implement that feature correctly. I "fixed" that in my local qemu code - I'll share the fix I used in a separate email.
-Kevin
commit 84a4d4b0b10c8a4e04c4e7bfd806c6c50693d945 Author: Kevin O'Connor kevin@koconnor.net Date: Thu Feb 11 22:32:12 2010 -0500
Support USB keyboard auto-repeat.
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.
diff --git a/src/biosvar.h b/src/biosvar.h index 2e43f8b..d011966 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -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 diff --git a/src/mouse.c b/src/mouse.c index 888d32d..3004d78 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -269,7 +269,7 @@ handle_15c2(struct bregs *regs) } }
-void +void noinline process_mouse(u8 data) { if (!CONFIG_MOUSE) diff --git a/src/usb-hid.c b/src/usb-hid.c index 7756c62..b714685 100644 --- a/src/usb-hid.c +++ b/src/usb-hid.c @@ -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 diff --git a/src/usb.c b/src/usb.c index 2bd5832..12747db 100644 --- 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);