// USB HID: keyboards, mice and absolute pointers. // // Keyboards are driven in boot protocol, which 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 one fixed layout on // request: eight bytes, defined in the HID specification and identical on // every keyboard. Nothing a keyboard sends beyond that is worth the parser. // // Pointers are not. The boot mouse report is three bytes -- buttons and an // 8-bit delta on each axis -- and has no wheel, which every mouse sold this // century has. So a pointer is driven in report protocol, from its report // descriptor: where its buttons, X, Y and wheel are, how wide, and whether // they are relative (a mouse) or absolute (a tablet, a touchscreen, a VM's // pointer). Only the fields a pointer needs are looked for; everything else // in the descriptor is stepped over. A boot mouse whose descriptor makes no // sense is still driven, in boot protocol, as before. #include "core/types.h" #include "core/defs.h" #include "gfx/gfx.h" #include "core/param.h" #include "core/spinlock.h" #include "core/dma.h" #include "dev/usb.h" #include "dev/input.h" #define NHID 8 // One field of a report, as the report descriptor placed it. struct hfield { int ok; int bit, size; // offset and width in bits, after any ID byte int lmin, lmax; // logical range; lmin < 0 means signed int rel; // relative (a delta) rather than absolute }; #define HID_NBTN 5 struct hid { int in_use; int proto; // USB_HID_PROTO_KEYBOARD, _MOUSE or _TABLET uchar prev[8]; // the previous report, for edge detection // A pointer in report protocol. `boot` instead means the three-byte boot // report, for a mouse whose descriptor could not be used. int boot; int report_id; // 0 when reports carry no ID byte struct hfield x, y, wheel, btn[HID_NBTN]; // A keyboard in report protocol: where its keys are, so that each report // can be rewritten into the boot layout and go through the same path. Not // every keyboard interface offers the boot subclass -- the second // interface of a gaming keyboard, the macro keys of a mouse, an NKRO mode // that reports a bitmap instead of six slots. int rkbd; // set when any of the below was found int kbd_id; // their report ID, 0 for none int mod_bit[8]; // E0-E7, -1 where the report has none int arr_bit, arr_n, arr_size; // an array of usages, arr_n slots int map_bit, map_first, map_n; // a bitmap: usage map_first + i }; 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; // The queue is filled from the interrupt handler and, for key repeat, from // the hot-plug thread; consoleintr() drains it from either. static struct spinlock kqlock; static int kqinit; // Software key repeat. A USB keyboard does not repeat: a held key is simply // still in the report, and the next report comes only when something // changes. The PS/2 keyboard repeats in its own firmware, so without this a // held key auto-repeats on one keyboard and not the other. #define REPEAT_DELAY (HZ / 2) // 500 ms, the PC default #define REPEAT_EVERY 3 // ticks: about 33 a second static struct { int c; // the character to repeat, 0 for none uchar usage; // the key producing it uint at; // tick of the next repeat } krep; static void kq_put(int c) { acquire(&kqlock); if(kq.w - kq.r < KQSIZE) kq.buf[kq.w++ % KQSIZE] = c; release(&kqlock); } static int usb_kbd_getc(void) { int c = -1; acquire(&kqlock); if(kq.r != kq.w) c = kq.buf[kq.r++ % KQSIZE]; release(&kqlock); return c; } // 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); // The newest key is the one that repeats, as on any keyboard. acquire(&kqlock); krep.c = c; krep.usage = usage; krep.at = ticks + REPEAT_DELAY; release(&kqlock); } // Released, or another key's report without it: no more repeats. acquire(&kqlock); if(krep.c && !was_down(rep, krep.usage)) krep.c = 0; release(&kqlock); memmove(h->prev, rep, 8); consoleintr(usb_kbd_getc); } void usb_hid_tick(void) { int c = 0; if(!kqinit) return; acquire(&kqlock); if(krep.c && (int)(ticks - krep.at) >= 0){ c = krep.c; krep.at = ticks + REPEAT_EVERY; if(kq.w - kq.r < KQSIZE) kq.buf[kq.w++ % KQSIZE] = c; } release(&kqlock); if(c && !input_is_grabbed(INPUT_KBD)) consoleintr(usb_kbd_getc); } // --------------------------------------------------------------------------- // Report descriptors // // A report descriptor is a sequence of items, each a prefix byte -- size in // bits 1:0 (3 meaning four bytes), type in 3:2, tag in 7:4 -- and that many // bytes of data. Global items (usage page, logical range, report size and // count, report ID) persist until changed; local items (usages) apply to the // next main item only. A main Input item lays down `count` fields of `size` // bits each, taking usages in order, and advances the position in that // report. Push, pop, delimiters and designators are not used by anything // that matters here and are passed over. // --------------------------------------------------------------------------- #define PAGE_DESKTOP 0x01 #define PAGE_KEYBOARD 0x07 #define PAGE_BUTTON 0x09 #define U_X 0x30 #define U_Y 0x31 #define U_WHEEL 0x38 static void take_field(struct hid *h, uint32 usage, int bit, int size, int lmin, int lmax, int rel, int id, int *target) { struct hfield *f = 0; int page = usage >> 16, u = usage & 0xFFFF; if(page == PAGE_DESKTOP && u == U_X) f = &h->x; else if(page == PAGE_DESKTOP && u == U_Y) f = &h->y; else if(page == PAGE_DESKTOP && u == U_WHEEL) f = &h->wheel; else if(page == PAGE_BUTTON && u >= 1 && u <= HID_NBTN) f = &h->btn[u - 1]; if(f == 0 || f->ok || size < 1 || size > 32) return; // Everything comes from one report: the first one with any of this in it. // A keyboard-and-mouse receiver describes both in one descriptor, and a // button bit from the keyboard's report would be nonsense in the mouse's. if(*target < 0) *target = id; if(id != *target) return; f->ok = 1; f->bit = bit; f->size = size; f->lmin = lmin; f->lmax = lmax; f->rel = rel; } // One Input item on the keyboard page. Everything is taken from the first // report ID that has any of it, for the same reason as take_field's. static void take_keys(struct hid *h, int variable, int bit, int size, int count, uint32 first, int id) { int k; if(h->rkbd && id != h->kbd_id) return; if(!variable){ // An array: `count` slots, each holding the usage of a key that is // down. The usage range only says which keys it can name. if(h->arr_n || size < 1 || size > 16) return; h->arr_bit = bit; h->arr_n = count; h->arr_size = size; } else { // One bit per key, the usage stepping up from `first`. Modifiers are // the common case; a whole keyboard's worth is NKRO. if(size != 1) return; for(k = 0; k < count; k++){ uint32 u = (first & 0xFFFF) + k; if(u >= 0xE0 && u <= 0xE7) h->mod_bit[u - 0xE0] = bit + k; else if(h->map_n == 0){ h->map_bit = bit + k; h->map_first = u; h->map_n = 1; } else if(bit + k == h->map_bit + h->map_n && (int)u == h->map_first + h->map_n) h->map_n++; } } h->rkbd = 1; h->kbd_id = id; } // Returns 0 if a pointer was found -- an X and a Y in the same report -- or a // keyboard. static int parse_report(struct hid *h, const uchar *d, int n) { uint32 page = 0, rsize = 0, rcount = 0, umin = 0, umax = 0; uint32 usage[16]; int lmin = 0, lmax = 0, id = 0, nusage = 0, range = 0, target = -1; int off[16]; int i = 0, k; memset(off, 0, sizeof(off)); for(k = 0; k < 8; k++) h->mod_bit[k] = -1; while(i < n){ uchar b = d[i++]; int sz = b & 3, type = (b >> 2) & 3, tag = b >> 4; uint32 v = 0; int sv; if(b == 0xFE){ // long item: size, tag, data if(i >= n) break; i += 2 + d[i]; continue; } if(sz == 3) sz = 4; if(i + sz > n) break; for(k = 0; k < sz; k++) v |= (uint32)d[i + k] << (8 * k); i += sz; sv = sz == 1 ? (signed char)v : sz == 2 ? (short)v : (int)v; if(type == 1){ // global switch(tag){ case 0: page = v; break; case 1: lmin = sv; break; // Signed or not depends on the minimum: 0..255 in one byte is 255, // not -1. case 2: lmax = sv < lmin ? (int)v : sv; break; case 7: rsize = v; break; case 8: id = v; break; case 9: rcount = v; break; } } else if(type == 2){ // local uint32 full = sz == 4 ? v : (page << 16) | v; switch(tag){ case 0: if(nusage < 16) usage[nusage++] = full; break; case 1: umin = full; range = 1; break; case 2: umax = full; break; } } else if(type == 0){ // main if(tag == 8 && id < 16 && rsize <= 32 && rcount <= 256 && page == PAGE_KEYBOARD){ uint32 first = range ? umin : nusage ? usage[0] : 0; if(!(v & 1)) take_keys(h, (v & 2) != 0, off[id], rsize, rcount, first, id); off[id] += rsize * rcount; } else if(tag == 8 && id < 16 && rsize <= 32 && rcount <= 256){ for(k = 0; k < (int)rcount; k++){ uint32 u = 0; if(k < nusage) u = usage[k]; else if(range && umin + (k - nusage) <= umax) u = umin + (k - nusage); else if(nusage > 0) u = usage[nusage - 1]; // Bit 0 set is a constant field: padding, there to be stepped over. if(!(v & 1)) take_field(h, u, off[id], rsize, lmin, lmax, (v & 4) != 0, id, &target); off[id] += rsize; } } nusage = 0; range = 0; } } if(!h->x.ok || !h->y.ok){ memset(&h->x, 0, sizeof(h->x)); // half a pointer is no pointer memset(&h->y, 0, sizeof(h->y)); return h->rkbd && (h->arr_n || h->map_n) ? 0 : -1; } h->report_id = target; return 0; } static uint32 bits(const uchar *p, int len, int bit, int size) { uint32 v = 0; int i; if((bit + size + 7) / 8 > len) return 0; for(i = 0; i < size; i++, bit++) if((p[bit >> 3] >> (bit & 7)) & 1) v |= 1u << i; return v; } // A report-protocol keyboard report, rewritten as a boot one: modifiers, a // reserved byte, six usages. Six is a limit only of the rewrite; the seventh // key held at once is dropped, which is what a boot keyboard does too. static void report_keyboard(struct hid *h, const uchar *rep, int len) { uchar boot[8]; int i, n = 0; if(h->kbd_id){ if(len < 1 || rep[0] != h->kbd_id) return; rep++; len--; } memset(boot, 0, sizeof(boot)); for(i = 0; i < 8; i++) if(h->mod_bit[i] >= 0 && bits(rep, len, h->mod_bit[i], 1)) boot[0] |= 1 << i; for(i = 0; i < h->arr_n; i++){ uint32 u = bits(rep, len, h->arr_bit + i * h->arr_size, h->arr_size); if(u >= 0xE0 && u <= 0xE7) boot[0] |= 1 << (u - 0xE0); else if(u >= 4 && u < 0xE0 && n < 6) // 1-3 are error codes boot[2 + n++] = u; } for(i = 0; i < h->map_n; i++){ uint32 u = h->map_first + i; if(u < 4 || u >= 0xE0 || !bits(rep, len, h->map_bit + i, 1)) continue; if(n < 6) boot[2 + n++] = u; } keyboard_report(h, boot, sizeof(boot)); } static int field(const uchar *p, int len, const struct hfield *f) { uint32 v = 0; int i; if(!f->ok || (f->bit + f->size + 7) / 8 > len) return 0; for(i = 0; i < f->size; i++){ int b = f->bit + i; if((p[b >> 3] >> (b & 7)) & 1) v |= 1u << i; } if(f->lmin < 0 && f->size < 32 && ((v >> (f->size - 1)) & 1)) v |= ~0u << f->size; // sign-extend return (int)v; } // --------------------------------------------------------------------------- // Pointers // --------------------------------------------------------------------------- static int scale(int v, const struct hfield *f, int pixels) { long long range = (long long)f->lmax - f->lmin; if(range <= 0) return 0; return (int)(((long long)(v - f->lmin) * (pixels - 1)) / range); } static void pointer_report(struct hid *h, const uchar *rep, int len) { int buttons = 0, i, wheel; if(h->boot){ // Boot protocol: buttons, then signed 8-bit X and Y. The deltas are // signed. Reading them as unsigned is the classic way to get a pointer // that only travels right and down. if(len < 3) return; mouse_inject((signed char)rep[1], (signed char)rep[2], rep[0] & 0x07); return; } if(h->report_id){ if(len < 1 || rep[0] != h->report_id) return; // some other report of the device's rep++; len--; } for(i = 0; i < HID_NBTN; i++) if(field(rep, len, &h->btn[i])) buttons |= 1 << i; // A wheel step is reported before the motion it came with, so that both // reach a reader in one batch: mouse_inject() is what ends the batch. wheel = field(rep, len, &h->wheel); if(wheel && h->wheel.rel) input_report(INPUT_MOUSE, EV_REL, REL_WHEEL, wheel); // 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. if(h->x.rel){ mouse_inject(field(rep, len, &h->x), field(rep, len, &h->y), buttons & 0x07); } else { // Scaled here because this is the only place that knows the logical // range; everything above deals in pixels. int w = (int)gfx_width(), hp = (int)gfx_height(); if(w <= 0) w = 800; if(hp <= 0) hp = 600; mouse_set_abs(scale(field(rep, len, &h->x), &h->x, w), scale(field(rep, len, &h->y), &h->y, hp), buttons & 0x07); } } // --------------------------------------------------------------------------- static void hid_complete(struct usb_dev *dev, void *buf, int len) { struct hid *h = dev->drvdata; if(h == 0) return; // One interface can be both: a wireless receiver describes its keyboard // and its mouse as two report IDs of one descriptor. if(h->rkbd) report_keyboard(h, buf, len); if(h->proto == USB_HID_PROTO_KEYBOARD && !h->rkbd) keyboard_report(h, buf, len); else if(h->x.ok || h->boot) pointer_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); } // The report descriptor's length is in the HID descriptor, which sits in the // configuration right after this interface's own descriptor. static int report_desc_len(struct usb_dev *dev) { int off = 0, mine = 0; while(off + 2 <= dev->conflen){ int len = dev->conf[off], type = dev->conf[off + 1]; if(len < 2 || off + len > dev->conflen) break; if(type == USB_DT_INTERFACE && len >= 4) mine = dev->conf[off + 2] == dev->iface.bInterfaceNumber && dev->conf[off + 3] == 0; else if(mine && type == USB_DT_HID && len >= 9 && dev->conf[off + 6] == USB_DT_REPORT) return dev->conf[off + 7] | (dev->conf[off + 8] << 8); off += len; } return -1; } static int read_report_desc(struct usb_dev *dev, struct hid *h) { struct usb_setup s; int len = report_desc_len(dev), rc; uchar *buf; if(len <= 0) return -1; if(len > 1024) len = 1024; buf = dma_alloc(len, 64); if(buf == 0) return -1; s.bmRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE; s.bRequest = USB_REQ_GET_DESCRIPTOR; s.wValue = USB_DT_REPORT << 8; s.wIndex = dev->iface.bInterfaceNumber; s.wLength = len; memset(buf, 0, len); rc = dev->ops->control(dev, &s, buf, len); if(rc >= 0) rc = parse_report(h, buf, len); dma_free(buf, len); return rc; } static int hid_probe(struct usb_dev *dev) { struct hid *h = 0; int i; int boot = dev->iface.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT; int proto = dev->iface.bInterfaceProtocol; if(!dev->has_intr_in){ cprintf("usb-hid: no interrupt IN endpoint; nothing to read\n"); return -1; } if(!kqinit){ initlock(&kqlock, "usbkbd"); kqinit = 1; } for(i = 0; i < NHID; i++) if(!hids[i].in_use){ h = &hids[i]; break; } if(h == 0){ cprintf("usb-hid: %d devices already; this one is not used\n", NHID); return -1; } memset(h, 0, sizeof(*h)); if(boot && proto == USB_HID_PROTO_KEYBOARD){ // 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. if(hid_request(dev, USB_HID_SET_PROTOCOL, USB_HID_PROTOCOL_BOOT) < 0){ cprintf("usb-hid: SET_PROTOCOL(boot) refused\n"); return -1; } h->proto = USB_HID_PROTO_KEYBOARD; } else if(read_report_desc(dev, h) == 0){ // Report protocol is the default after a reset, but a firmware that used // the mouse may have left it in boot protocol, and a port reset is not // always a device reset. Only a boot device knows the request. if(boot) hid_request(dev, USB_HID_SET_PROTOCOL, USB_HID_PROTOCOL_REPORT); h->proto = !h->x.ok ? USB_HID_PROTO_KEYBOARD : h->x.rel ? USB_HID_PROTO_MOUSE : USB_HID_PROTO_TABLET; } else if(boot && proto == USB_HID_PROTO_MOUSE){ memset(h, 0, sizeof(*h)); if(hid_request(dev, USB_HID_SET_PROTOCOL, USB_HID_PROTOCOL_BOOT) < 0){ cprintf("usb-hid: SET_PROTOCOL(boot) refused\n"); return -1; } h->proto = USB_HID_PROTO_MOUSE; h->boot = 1; } else { // A consumer-control interface (volume keys), a game pad, a UPS: HID, // and not something this driver has a use for. cprintf("usb-hid: interface %d is neither a keyboard nor a pointer\n", dev->iface.bInterfaceNumber); 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; 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){ h->in_use = 0; dev->drvdata = 0; return -1; } cprintf("usb-hid: %s%s on slot %d, endpoint 0x%x every %d ms\n", h->proto == USB_HID_PROTO_KEYBOARD ? "keyboard" : h->proto == USB_HID_PROTO_MOUSE ? "mouse" : "absolute pointer", h->proto == USB_HID_PROTO_KEYBOARD ? (h->rkbd ? " (report protocol)" : "") : h->boot ? "" : h->rkbd ? " and keyboard" : h->wheel.ok ? " with wheel" : "", dev->slot, dev->intr_in.bEndpointAddress, dev->intr_in.bInterval); return 0; } // Unplugged. A key held at that moment is released, so that a reader of // /dev/input does not see it held for ever, and the slot is given back. static void hid_disconnect(struct usb_dev *dev) { struct hid *h = dev->drvdata; static const uchar none[8]; if(h == 0) return; if(h->proto == USB_HID_PROTO_KEYBOARD || h->rkbd){ keyboard_events(h->prev, none); acquire(&kqlock); krep.c = 0; release(&kqlock); } cprintf("usb-hid: %s on slot %d unplugged\n", h->proto == USB_HID_PROTO_KEYBOARD ? "keyboard" : "pointer", dev->slot); h->in_use = 0; dev->drvdata = 0; dev->complete = 0; } // Any HID interface, not just the boot subclass: hid_probe decides what a // given interface actually is, and declines anything it cannot make sense of. const struct usb_driver usb_hid_driver = { .name = "usb-hid", .class = USB_CLASS_HID, .subclass = 0xFF, .protocol = 0xFF, .devclass = DEVCLASS_INPUT, .probe = hid_probe, .disconnect = hid_disconnect, };