SchOS/kernel/drivers/usb/hid/usb_hid.c
Nexoniarz c65bfa3fa3 boot: driver chatter goes to the log; the console clears its margins
ACPI, DMI, SMP, xHCI, USB, I2C, NVMe, r8169 and ramdisk progress lines are
kinfo now: in dmesg and on a verbose boot, off the screen of a working
machine. Failures stay cprintf.

vt_repaint also paints the strip right of the last column and below the
last row, which kept the bottom of the X desktop after Ctrl+Alt+F-key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-25 01:53:46 +02:00

927 lines
28 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 "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"
#include "dev/hid.h"
#include "dev/kbd.h"
#include "dev/vt.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
// 1 + the only report ID to take pointer fields from, 0 for any; and
// 1 + the first report ID with a relative X. A touchpad describes both a
// mouse report and a multi-finger one with absolute coordinates, and only
// the mouse report makes sense as a pointer; see hid_attach().
int only_id1, rel_id1;
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 {
int buf[KQSIZE]; // characters, and KBD_* codes past KBD_SPECIAL
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?
// The keys past the character table, as the console names them. The PS/2
// path has always produced these (kernel/dev/kbd.c); this one skipped every
// usage from 0x39 up, so on a USB keyboard the console had no arrows, no
// Home/End/PgUp/PgDn/Delete, and no way to switch terminals.
static int
hid_special(uchar usage, uchar mods)
{
// F1..F6 with Alt, AltGr or Ctrl+Alt: switch terminals, as on Linux.
if (usage >= 0x3A && usage < 0x3A + NVT && (mods & HID_MOD_ALT) &&
!(mods & HID_MOD_SHIFT))
return KBD_VT_SWITCH + (usage - 0x3A);
switch (usage) {
case 0x49: return KBD_INS;
case 0x4A: return KBD_HOME;
case 0x4B: return KBD_PGUP;
case 0x4C: return KBD_DEL;
case 0x4D: return KBD_END;
case 0x4E: return KBD_PGDN;
case 0x4F: return KBD_RT;
case 0x50: return KBD_LF;
case 0x51: return KBD_DN;
case 0x52: return KBD_UP;
}
return 0;
}
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)
continue;
if(was_down(h->prev, usage))
continue; // still held from last time, not a new press
if(usage >= 0x39){
if((c = hid_special(usage, mods)) == 0)
continue;
} else {
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) && c < KBD_SPECIAL){
if(c >= 'a' && c <= 'z')
c = c - 'a' + 1;
else if(c >= 'A' && c <= 'Z')
c = c - 'A' + 1;
else
continue;
}
kq_put(c);
if(c >= KBD_VT_SWITCH && c < KBD_VT_SWITCH + NVT)
continue; // a held chord switches once
// 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 && rel && h->rel_id1 == 0)
h->rel_id1 = id + 1;
if(h->only_id1 && id + 1 != h->only_id1)
return;
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.
// Nothing else happens here, which is what lets GMMOS call it after a panic.
static int
to_boot(struct hid *h, const uchar *rep, int len, uchar *boot)
{
int i, n = 0;
if(h->kbd_id){
if(len < 1 || rep[0] != h->kbd_id)
return 0;
rep++;
len--;
}
memset(boot, 0, 8);
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;
}
return 1;
}
static void
report_keyboard(struct hid *h, const uchar *rep, int len)
{
uchar boot[8];
if(to_boot(h, rep, len, boot))
keyboard_report(h, boot, sizeof(boot));
}
int
hid_is_keyboard(void *hv)
{
struct hid *h = hv;
return h && (h->rkbd || h->proto == USB_HID_PROTO_KEYBOARD);
}
int
hid_boot_report(void *hv, const uchar *rep, int len, uchar *boot)
{
struct hid *h = hv;
if(h->rkbd)
return to_boot(h, rep, len, boot);
if(h->proto != USB_HID_PROTO_KEYBOARD || len < 8)
return 0;
memmove(boot, rep, 8);
return 1;
}
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_input(struct hid *h, const uchar *buf, int len)
{
// 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 void
hid_complete(struct usb_dev *dev, void *buf, int len)
{
struct hid *h = dev->drvdata;
if(h != 0)
hid_input(h, buf, len);
}
static struct hid *
hid_slot(void)
{
int i;
if(!kqinit){
initlock(&kqlock, "usbkbd");
kqinit = 1;
}
for(i = 0; i < NHID; i++)
if(!hids[i].in_use){
memset(&hids[i], 0, sizeof(hids[i]));
return &hids[i];
}
return 0;
}
// A HID device on some other transport -- I2C, for a laptop's touchpad --
// handing over its report descriptor. Keyboards and pointers come out of it
// exactly as from USB. A descriptor with a relative pointer is driven
// through that report alone, even if an absolute one comes first: that is a
// touchpad's mouse report, which it sends until told to report fingers.
void *
hid_attach(const uchar *desc, int n, const char *what)
{
struct hid *h = hid_slot();
int rel;
if(h == 0)
return 0;
if(parse_report(h, desc, n) < 0)
return 0;
rel = h->rel_id1;
if(h->x.ok && !h->x.rel && rel){
memset(h, 0, sizeof(*h));
h->only_id1 = rel;
if(parse_report(h, desc, n) < 0)
return 0;
}
h->proto = !h->x.ok ? USB_HID_PROTO_KEYBOARD :
h->x.rel ? USB_HID_PROTO_MOUSE : USB_HID_PROTO_TABLET;
h->in_use = 1;
kinfo("%s: %s%s\n", what,
h->proto == USB_HID_PROTO_KEYBOARD ? "keyboard" :
h->proto == USB_HID_PROTO_MOUSE ? "pointer" : "absolute pointer",
h->rkbd && h->x.ok ? " and keyboard" : "");
return h;
}
void
hid_attached_input(void *h, const uchar *rep, int len)
{
hid_input(h, rep, 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((h = hid_slot()) == 0){
cprintf("usb-hid: %d devices already; this one is not used\n", NHID);
return -1;
}
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.
kinfo("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;
}
kinfo("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);
}
kinfo("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,
};