// USB HID, boot protocol: keyboards and mice. // // Boot protocol is the reason a USB keyboard works in a BIOS setup screen // before any operating system exists. A HID device normally describes its // reports with a report descriptor -- a small stack language that has to be // parsed to know which bit means which key -- but a device claiming the boot // subclass also promises to support one fixed layout on request. Eight bytes // for a keyboard, three for a mouse, defined in the HID specification and // identical on every device. // // That is worth taking. A full report-descriptor parser is a real piece of // work and buys nothing for a keyboard; it is what a game controller or a // touchscreen will need, and it can arrive with them. #include "types.h" #include "defs.h" #include "param.h" #include "spinlock.h" #include "dma.h" #include "usb.h" #include "input.h" #define NHID 4 struct hid { int in_use; int proto; // USB_HID_PROTO_KEYBOARD or _MOUSE uchar prev[8]; // the previous report, for edge detection }; static struct hid hids[NHID]; // --------------------------------------------------------------------------- // Keyboard // // The report is: modifiers, a reserved byte, then up to six usage IDs of keys // currently held. It is a *state*, not an event -- a key that stays down // appears in every report until it is released, so a character is emitted // only for usages that were not in the previous report. Sending one per // report instead would repeat every held key a few hundred times a second. // --------------------------------------------------------------------------- #define HID_MOD_CTRL 0x11 // left | right #define HID_MOD_SHIFT 0x22 #define HID_MOD_ALT 0x44 // Usage IDs 0x00-0x38 of the Keyboard/Keypad page. 0 means "nothing to type". static const uchar hid_unshifted[0x39] = { 0, 0, 0, 0, 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '1','2','3','4','5','6','7','8','9','0', '\n', 0x1B, '\b', '\t', ' ', '-','=','[',']','\\','#',';','\'','`',',','.','/', }; static const uchar hid_shifted[0x39] = { 0, 0, 0, 0, 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '!','@','#','$','%','^','&','*','(',')', '\n', 0x1B, '\b', '\t', ' ', '_','+','{','}','|','~',':','"','~','<','>','?', }; // Characters decoded from a report, waiting to be handed to the console. // // The console pulls input through a getc function rather than being pushed // to (consoleintr takes one), which is exactly the shape the PS/2 driver // needs and exactly the wrong shape here -- a USB report arrives complete, // with up to six keys in it. So the report is decoded into this queue and // the console then drains it through the same path as any other keyboard, // which means the entire line discipline is reused rather than reimplemented. #define KQSIZE 64 static struct { uchar buf[KQSIZE]; uint r, w; } kq; static void kq_put(int c) { if(kq.w - kq.r < KQSIZE) kq.buf[kq.w++ % KQSIZE] = c; } static int usb_kbd_getc(void) { if(kq.r == kq.w) return -1; return kq.buf[kq.r++ % KQSIZE]; } // Was this usage in that report? static int was_down(const uchar *prev, uchar usage) { int i; for(i = 2; i < 8; i++) if(prev[i] == usage) return 1; return 0; } // Turn the difference between two reports into press and release events for // /dev/input/event0. // // The console path below cannot do this job: it emits a character per new key // and nothing at all for a release, because a character stream has no way to // say "released". A program that wants to know a key is being *held* -- which // is every game and every compositor -- needs exactly the events this makes. static void keyboard_events(const uchar *prev, const uchar *rep) { int i, bit, n = 0; for(bit = 0; bit < 8; bit++){ int now = (rep[0] >> bit) & 1; int then = (prev[0] >> bit) & 1; if(now != then){ input_report(INPUT_KBD, EV_KEY, input_hid_keycode(0xE0 + bit), now); n++; } } for(i = 2; i < 8; i++){ uchar u = rep[i]; if(u && !was_down(prev, u)){ int k = input_hid_keycode(u); if(k){ input_report(INPUT_KBD, EV_KEY, k, 1); n++; } } } for(i = 2; i < 8; i++){ uchar u = prev[i]; if(u && !was_down(rep, u)){ int k = input_hid_keycode(u); if(k){ input_report(INPUT_KBD, EV_KEY, k, 0); n++; } } } if(n) input_sync(INPUT_KBD); } static void keyboard_report(struct hid *h, const uchar *rep, int len) { uchar mods = rep[0]; int i, c; if(len < 8) return; keyboard_events(h->prev, rep); // Grabbed: the events above still went to /dev/input/event0, but nothing // reaches the console. Note prev[] is updated before returning, or the next // report would look like every held key had just been pressed again. if(input_is_grabbed(INPUT_KBD)){ memmove(h->prev, rep, 8); return; } for(i = 2; i < 8; i++){ uchar usage = rep[i]; if(usage == 0 || usage >= 0x39) continue; if(was_down(h->prev, usage)) continue; // still held from last time, not a new press c = (mods & HID_MOD_SHIFT) ? hid_shifted[usage] : hid_unshifted[usage]; if(c == 0) continue; // Ctrl folds a letter down into its control character, which is how ^C // and ^D reach the line discipline at all. if(mods & HID_MOD_CTRL){ if(c >= 'a' && c <= 'z') c = c - 'a' + 1; else if(c >= 'A' && c <= 'Z') c = c - 'A' + 1; else continue; } kq_put(c); } memmove(h->prev, rep, 8); consoleintr(usb_kbd_getc); } // --------------------------------------------------------------------------- // Mouse // --------------------------------------------------------------------------- // QEMU's usb-tablet, and absolute pointers generally. // // Report: one byte of buttons, then X and Y as 16-bit little-endian values // over the device's logical range, then a wheel byte. The range is 0..0x7fff // and is *not* read from the report descriptor -- parsing one is a real piece // of work, and this decodes exactly the fixed layout the tablet declares. // A device whose report is a different length is left alone rather than // guessed at. // // Scaling happens here because this is the only place that knows the logical // range; everything above deals in pixels. #define TABLET_LOGICAL_MAX 0x7fff static void tablet_report(struct hid *h, const uchar *rep, int len) { (void)h; if(len < 5) return; int rawx = rep[1] | (rep[2] << 8); int rawy = rep[3] | (rep[4] << 8); int w = (int)vga_get_width(); int h_px = (int)vga_get_height(); if(w <= 0) w = 800; if(h_px <= 0) h_px = 600; // Scale in 64-bit-free arithmetic: 0x7fff * 4096 still fits in an int. int x = (int)(((long long)rawx * (w - 1)) / TABLET_LOGICAL_MAX); int y = (int)(((long long)rawy * (h_px - 1)) / TABLET_LOGICAL_MAX); mouse_set_abs(x, y, rep[0] & 0x07); } static void mouse_report(struct hid *h, const uchar *rep, int len) { int dx, dy; (void)h; if(len < 3) return; // The deltas are signed bytes. Reading them as unsigned is the classic way // to get a pointer that only travels right and down. dx = (signed char)rep[1]; dy = (signed char)rep[2]; // HID's Y axis points down, and so does the screen's. The PS/2 protocol's // points up, which is why mouseintr subtracts where this adds. mouse_inject(dx, dy, rep[0] & 0x07); } // --------------------------------------------------------------------------- static void hid_complete(struct usb_dev *dev, void *buf, int len) { struct hid *h = dev->drvdata; if(h == 0) return; if(h->proto == USB_HID_PROTO_TABLET) tablet_report(h, buf, len); else if(h->proto == USB_HID_PROTO_KEYBOARD) keyboard_report(h, buf, len); else mouse_report(h, buf, len); } static int hid_request(struct usb_dev *dev, int request, int value) { struct usb_setup s; s.bmRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; s.bRequest = request; s.wValue = value; s.wIndex = dev->iface.bInterfaceNumber; s.wLength = 0; return dev->ops->control(dev, &s, 0, 0); } static int hid_probe(struct usb_dev *dev) { struct hid *h = 0; int i, proto = dev->iface.bInterfaceProtocol; if(!dev->has_intr_in){ cprintf("usb-hid: no interrupt IN endpoint; nothing to read\n"); return -1; } for(i = 0; i < NHID; i++) if(!hids[i].in_use){ h = &hids[i]; break; } if(h == 0) return -1; // A HID device that claims no boot protocol is taken to be an absolute // pointer -- in practice a tablet, which is what a VM offers so that its // cursor and the host's can agree. It must not be asked for boot protocol: // it has none, the request fails, and refusing on that basis is what kept // usb-tablet from binding at all. if(proto != USB_HID_PROTO_KEYBOARD && proto != USB_HID_PROTO_MOUSE) proto = USB_HID_PROTO_TABLET; // Ask for the fixed layout explicitly. A device that supports the boot // subclass still starts in report protocol, so without this the reports // arrive in whatever shape its report descriptor declares -- which we have // not read and cannot interpret. if(proto != USB_HID_PROTO_TABLET && hid_request(dev, USB_HID_SET_PROTOCOL, USB_HID_PROTOCOL_BOOT) < 0){ cprintf("usb-hid: SET_PROTOCOL(boot) refused\n"); return -1; } // Idle rate 0 = "report only when something changes". The default is to // resend the current state every few milliseconds forever, which for a // keyboard means a constant stream of interrupts saying nothing. hid_request(dev, USB_HID_SET_IDLE, 0); if(dev->ops->open_ep(dev, &dev->intr_in) < 0) return -1; h->in_use = 1; h->proto = proto; dev->drvdata = h; dev->complete = hid_complete; // Arm the first transfer. Until a TRB is waiting on the endpoint the device // has nowhere to put a report, so nothing would ever arrive. if(dev->ops->queue_in(dev, dev->intr_in.bEndpointAddress, 0, 0) < 0) return -1; cprintf("usb-hid: %s on slot %d, endpoint 0x%x every %d ms\n", proto == USB_HID_PROTO_KEYBOARD ? "keyboard" : proto == USB_HID_PROTO_MOUSE ? "mouse" : proto == USB_HID_PROTO_TABLET ? "tablet (absolute)" : "device", dev->slot, dev->intr_in.bEndpointAddress, dev->intr_in.bInterval); return 0; } // Any HID interface, not just the boot subclass. // // It was boot-only, on the reasoning that a device without boot protocol needs // its report descriptor parsed and there would be nothing useful to do with it. // That is right in general and wrong for the one device that matters here: // usb-tablet declares subclass 0, so it was refused at the door -- and its // report layout is fixed and known, so there is something useful to do with it // after all. hid_probe decides what a given interface actually is; anything it // cannot make sense of is still declined there. const struct usb_driver usb_hid_driver = { .name = "usb-hid", .class = USB_CLASS_HID, .subclass = 0xFF, .protocol = 0xFF, .devclass = DEVCLASS_INPUT, .probe = hid_probe, };