X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fusb-hid.c;h=168b7fabb209588fffe89bdfd79fdfc8c3f98233;hb=47c8e31d36fc79d694d430169c401297abd15440;hp=c849aa151c23288d1b3be3818b4132ea80732ea3;hpb=1c46a548f26cd28d478ae793e42bc1a82a977939;p=seabios.git diff --git a/src/usb-hid.c b/src/usb-hid.c index c849aa1..168b7fa 100644 --- a/src/usb-hid.c +++ b/src/usb-hid.c @@ -9,16 +9,19 @@ #include "config.h" // CONFIG_* #include "usb.h" // usb_ctrlrequest #include "biosvar.h" // GET_GLOBAL +#include "ps2port.h" // ATKBD_CMD_GETID struct usb_pipe *keyboard_pipe VAR16VISIBLE; +struct usb_pipe *mouse_pipe VAR16VISIBLE; /**************************************************************** * Setup ****************************************************************/ +// Send USB HID protocol message. static int -set_protocol(u32 endp, u16 val) +set_protocol(struct usb_pipe *pipe, u16 val) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; @@ -26,72 +29,105 @@ set_protocol(u32 endp, u16 val) req.wValue = val; req.wIndex = 0; req.wLength = 0; - return send_default_control(endp, &req, NULL); + return send_default_control(pipe, &req, NULL); } +// Send USB HID SetIdle request. static int -set_idle(u32 endp, u8 val) +set_idle(struct usb_pipe *pipe, 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); + return send_default_control(pipe, &req, NULL); } -int -usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax) +#define KEYREPEATWAITMS 500 +#define KEYREPEATMS 33 + +static int +usb_kbd_init(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) { if (! CONFIG_USB_KEYBOARD) return -1; if (keyboard_pipe) + // XXX - this enables the first found keyboard (could be random) return -1; - dprintf(2, "usb_keyboard_setup %x\n", endp); - struct usb_endpoint_descriptor *epdesc = (void*)&iface[1]; - for (;;) { - if ((void*)epdesc >= (void*)iface + imax - || epdesc->bDescriptorType == USB_DT_INTERFACE) { - dprintf(1, "No keyboard intr in?\n"); - return -1; - } - if (epdesc->bDescriptorType == USB_DT_ENDPOINT - && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN - && ((epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT) - && epdesc->wMaxPacketSize == 8) - break; - epdesc = (void*)epdesc + epdesc->bLength; - } - u32 inendp = mkendp(endp2cntl(endp), endp2devaddr(endp) - , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK - , endp2speed(endp), epdesc->wMaxPacketSize); + if (epdesc->wMaxPacketSize != 8) + return -1; // Enable "boot" protocol. - int ret = set_protocol(endp, 1); + int ret = set_protocol(pipe, 0); if (ret) return -1; - // Only send reports on a new key event. - ret = set_idle(endp, 0); + // Periodically send reports to enable key repeat. + ret = set_idle(pipe, KEYREPEATMS); if (ret) return -1; - struct usb_pipe *pipe = alloc_intr_pipe(inendp, epdesc->bInterval); - if (!pipe) + keyboard_pipe = alloc_intr_pipe(pipe, epdesc); + if (!keyboard_pipe) return -1; - keyboard_pipe = pipe; + dprintf(1, "USB keyboard initialized\n"); return 0; } -void -usb_keyboard_setup() +static int +usb_mouse_init(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) { - if (! CONFIG_USB_KEYBOARD) - return; - keyboard_pipe = NULL; + if (! CONFIG_USB_MOUSE) + return -1; + if (mouse_pipe) + // XXX - this enables the first found mouse (could be random) + return -1; + + if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(pipe, 0); + if (ret) + return -1; + + mouse_pipe = alloc_intr_pipe(pipe, epdesc); + if (!mouse_pipe) + return -1; + + dprintf(1, "USB mouse initialized\n"); + return 0; +} + +// Initialize a found USB HID device (if applicable). +int +usb_hid_init(struct usb_pipe *pipe + , struct usb_interface_descriptor *iface, int imax) +{ + if (! CONFIG_USB_KEYBOARD || ! CONFIG_USB_MOUSE) + return -1; + dprintf(2, "usb_hid_init %p\n", pipe); + + if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT) + // Doesn't support boot protocol. + return -1; + + // Find intr in endpoint. + struct usb_endpoint_descriptor *epdesc = findEndPointDesc( + iface, imax, USB_ENDPOINT_XFER_INT, USB_DIR_IN); + if (!epdesc) { + dprintf(1, "No usb hid intr in?\n"); + return -1; + } + + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD) + return usb_kbd_init(pipe, epdesc); + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return usb_mouse_init(pipe, epdesc); + return -1; } @@ -99,6 +135,7 @@ usb_keyboard_setup() * Keyboard events ****************************************************************/ +// Mapping from USB key id to ps2 key sequence. static u16 KeyToScanCode[] VAR16 = { 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020, 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, @@ -115,17 +152,22 @@ static u16 KeyToScanCode[] VAR16 = { 0x0048, 0x0049, 0x0052, 0x0053 }; +// Mapping from USB modifier id to ps2 key sequence. static u16 ModifierToScanCode[] VAR16 = { //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c }; +#define RELEASEBIT 0x80 + +// Format of USB keyboard event data struct keyevent { u8 modifiers; u8 reserved; u8 keys[6]; }; +// Translate data from KeyToScanCode[] to calls to process_key(). static void prockeys(u16 keys) { @@ -134,8 +176,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); @@ -143,31 +185,98 @@ prockeys(u16 keys) process_key(keys); } +// Handle a USB key press/release event. +static void +procscankey(u8 key, u8 flags) +{ + if (key >= ARRAY_SIZE(KeyToScanCode)) + return; + u16 keys = GET_GLOBAL(KeyToScanCode[key]); + if (keys) + prockeys(keys | flags); +} + +// Handle a USB modifier press/release event. static void +procmodkey(u8 mods, u8 flags) +{ + int i; + for (i=0; mods; i++) + if (mods & (1<modifiers, data->keys[0]); - // XXX + dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]); + + // 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<=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; ikeys); 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<buttons, data->x, data->y); + + s8 x = data->x, y = -data->y; + u8 flag = ((data->buttons & 0x7) | (1<<3) + | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0)); + process_mouse(flag); + process_mouse(x); + process_mouse(y); +} + +// Check if a USB mouse event is pending and process it if so. +static void +usb_check_mouse(void) +{ + if (! CONFIG_USB_MOUSE) + return; + struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe); + if (!pipe) + return; + + for (;;) { + struct mouseevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_mouse(&data); + } +} + +// Test if USB mouse is active. +inline int +usb_mouse_active(void) +{ + if (! CONFIG_USB_MOUSE) + return 0; + return GET_GLOBAL(mouse_pipe) != NULL; +} + +// Handle a ps2 style mouse command. +inline int +usb_mouse_command(int command, u8 *param) +{ + if (! CONFIG_USB_MOUSE) + return -1; + dprintf(9, "usb mouse cmd=%x\n", command); + switch (command) { + case PSMOUSE_CMD_ENABLE: + case PSMOUSE_CMD_DISABLE: + case PSMOUSE_CMD_SETSCALE11: + return 0; + case PSMOUSE_CMD_SETSCALE21: + case PSMOUSE_CMD_SETRATE: + case PSMOUSE_CMD_SETRES: + // XXX + return 0; + case PSMOUSE_CMD_RESET_BAT: + case PSMOUSE_CMD_GETID: + // Return the id of a standard AT mouse. + param[0] = 0xaa; + param[1] = 0x00; + return 0; + + case PSMOUSE_CMD_GETINFO: + param[0] = 0x00; + param[1] = 4; + param[2] = 100; + return 0; + + default: + return -1; + } +} + +// Check for USB events pending - called periodically from timer interrupt. +void +usb_check_event(void) +{ + usb_check_key(); + usb_check_mouse(); +}