SchOS/kernel/drivers/usb/hid/usb_hid.c
Nexoniarz 27bf91cfe0 usb: recover endpoint 0 after a failed request; report-protocol keyboards
From the first boot on real hardware: a Razer mouse's control transfer
failed with a babble, and an ITE controller's late endpoint-0 completion
was reported as an "unexpected event" during a command.

A failed control transfer left endpoint 0 halted (or, on a timeout, still
running with TRBs queued), so every request after it to that device failed
too. It is now reset or stopped and its dequeue pointer moved past what was
left on the ring. A STALL -- a device declining a request it does not
implement -- is logged only on a verbose boot.

HID interfaces without the boot subclass were turned away unless they had a
pointer. The report descriptor's keyboard page is now read too -- modifier
bits, a key array, an NKRO bitmap -- and each report rewritten into the boot
layout for the existing path. Checked with QEMU's usb-kbd forced into report
protocol: "echo Hi", Shift included, typed and ran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-22 23:47:17 +02:00

801 lines
24 KiB
C

// 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 "types.h"
#include "defs.h"
#include "gfx.h"
#include "param.h"
#include "spinlock.h"
#include "dma.h"
#include "usb.h"
#include "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,
};