- SIGSTOP/SIGTSTP/SIGTTIN/SIGTTOU stop, SIGCONT resumes; the parent gets SIGCHLD and wait4 reports WUNTRACED/WCONTINUED; ps shows T. Ctrl-Z works, and mc no longer hangs waiting for its subshell to stop. - Blocking reads (console, pty, pipe) and sleeps take a pending stop and carry on after SIGCONT; a caught signal without SA_RESTART ends them with EINTR. - nanosleep reads tv_nsec (sleep 0.5 used to return at once), writes the remainder, and clock_nanosleep understands TIMER_ABSTIME. - rt_sigaction uses the k_sigaction layout for Linux-ABI processes: the old action's restorer was written 112 bytes past the caller's struct. Flags are kept; SIGKILL and SIGSTOP cannot be caught. - F1..F12 reach programs, as the Linux console sends them (PS/2, USB, I2C). - TIOCLINUX subcode 6 answers "no modifiers" instead of echoing the 6 back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
929 lines
28 KiB
C
929 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);
|
|
if (usage >= 0x3A && usage <= 0x45)
|
|
return KBD_F1 + (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,
|
|
};
|