ref: 46a7d83a9a58bdaf18045d7e76890e4b636e1a8e
parent: 8f211091301af3a465be701bfdfecf049733dc20
author: qwx <qwx@sciops.net>
date: Wed Jun 12 22:19:00 EDT 2024
nusb/kb: add temp stupid tablet workaround
--- /dev/null
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -1,0 +1,1123 @@
+/*
+ * USB Human Interaction Device: keyboard and mouse.
+ *
+ * If there's no usb keyboard, it tries to setup the mouse, if any.
+ * It should be started at boot time.
+ *
+ * Mouse events are converted to the format of mouse(3)
+ * on mousein file.
+ * Keyboard keycodes are translated to scan codes and sent to kbdfs(8)
+ * on kbin file.
+ *
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "usb.h"
+#include "hid.h"
+
+enum
+{
+ Awakemsg = 0xdeaddead,
+ Diemsg = 0xbeefbeef,
+ Rawon = 0x0defaced,
+ Dwcidle = 8,
+};
+
+char user[] = "kb";
+
+typedef struct Hiddev Hiddev;
+struct Hiddev
+{
+ Dev* dev; /* usb device*/
+ Dev* ep; /* endpoint to get events */
+
+ int minfd;
+ int kinfd;
+
+ int idle;
+
+ Channel *repeatc; /* only for keyboard */
+
+ /* report descriptor */
+ int nrep;
+
+ /*
+ * use odd size as some devices ignore the high byte of
+ * wLength in control transfer reads.
+ */
+ uchar rep[1024-1];
+};
+
+typedef struct Hidreport Hidreport;
+typedef struct Hidslot Hidslot;
+struct Hidslot
+{
+ int valid;
+ int usage;
+ int id;
+ int oor;
+
+ int abs; /* for xyz */
+
+ int x;
+ int y;
+ int z;
+
+ int b;
+ int m;
+
+ int w;
+ int h;
+};
+
+struct Hidreport
+{
+ int ns;
+ Hidslot s[16];
+
+ int nk;
+ ushort k[Nkey];
+
+ int o;
+ uchar *e;
+ uchar p[128];
+};
+
+/*
+ * Plan 9 keyboard driver constants.
+ */
+enum {
+ /* Scan codes (see kbd.c) */
+ SCesc1 = 0xe0, /* first of a 2-character sequence */
+ SCesc2 = 0xe1,
+ Consumer = 0x100, /* the key is on consumer page */
+ Keyup = 0x80, /* flag bit */
+ Keymask = 0x7f, /* regular scan code bits */
+};
+
+/*
+ * scan codes >= 0x80 are extended (E0 XX)
+ */
+#define isext(sc) ((sc) >= 0x80)
+
+static char sctab[2*256] =
+{
+/*
+ * key code to scan code; for the page table used by
+ * the logitech bluetooth keyboard.
+ */
+[0x00] 0x0, 0x0, 0x0, 0x0, 0x1e, 0x30, 0x2e, 0x20,
+[0x08] 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
+[0x10] 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14,
+[0x18] 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x2, 0x3,
+[0x20] 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
+[0x28] 0x1c, 0x1, 0xe, 0xf, 0x39, 0xc, 0xd, 0x1a,
+[0x30] 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34,
+[0x38] 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+[0x40] 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xe3, 0x46,
+[0x48] 0xf7, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd,
+[0x50] 0xcb, 0xd0, 0xc8, 0x45, 0x35, 0x37, 0x4a, 0x4e,
+[0x58] 0x1c, 0xcf, 0xd0, 0xd1, 0xcb, 0xcc, 0xcd, 0xc7,
+[0x60] 0xc8, 0xc9, 0xd2, 0xd3, 0x56, 0xff, 0xf4, 0xf5,
+[0x68] 0xd5, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+[0x70] 0xf8, 0xf9, 0xfa, 0xfb, 0x0, 0x0, 0x0, 0x0,
+[0x78] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf1,
+[0x80] 0xf3, 0xf2, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0,
+[0x88] 0x70, 0x0, 0x79, 0x7b, 0x0, 0x0, 0x0, 0x0,
+[0x90] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x98] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xa0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xa8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xb0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xb8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xc0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xc8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xd0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xd8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xe0] 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xfe,
+[0xe8] 0x0, 0x0, 0x0, 0x0, 0x0, 0xf3, 0xf2, 0xf1,
+[0xf0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+/* consumer page to scan code */
+[0x100] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x108] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x110] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x118] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x120] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x128] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x130] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x138] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x140] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x148] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x150] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x158] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x160] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x168] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9a,
+[0x170] 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x178] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x180] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x188] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x190] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x198] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1a0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1a8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1b0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x99, 0x90, 0x0,
+[0x1b8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1c0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1c8] 0x0, 0x0, 0x0, 0x0, 0x0, 0xa2, 0x0, 0x0,
+[0x1d0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1d8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1e0] 0x0, 0x0, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1e8] 0x0, 0xb0, 0xae, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1f0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+[0x1f8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+};
+
+static uchar kbdbootrep[] = {
+0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07,
+0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01,
+0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01,
+0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01,
+0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02,
+0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06,
+0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07,
+0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xc0,
+};
+
+static uchar ptrbootrep[] = {
+0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01,
+0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
+0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
+0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
+0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38,
+0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03,
+0x81, 0x06, 0xc0, 0x09, 0x3c, 0x15, 0x00, 0x25,
+0x01, 0x75, 0x01, 0x95, 0x01, 0xb1, 0x22, 0x95,
+0x07, 0xb1, 0x01, 0xc0,
+};
+
+static int debug = 0;
+static int kbdelay = 500;
+static int kbrepeat = 100;
+static int havekbd;
+
+static int
+signext(int v, int bits)
+{
+ int s;
+
+ s = sizeof(v)*8 - bits;
+ v <<= s;
+ v >>= s;
+ return v;
+}
+
+static int
+getbits(uchar *p, uchar *e, int bits, int off)
+{
+ int v, m;
+
+ p += off/8;
+ off %= 8;
+ v = 0;
+ m = 1;
+ if(p < e){
+ while(bits--){
+ if(*p & (1<<off))
+ v |= m;
+ if(++off == 8){
+ if(++p >= e)
+ break;
+ off = 0;
+ }
+ m <<= 1;
+ }
+ }
+ return v;
+}
+
+enum {
+ Ng = RepCnt+1,
+ UsgCnt = Delim+1, /* fake */
+ Nl = UsgCnt+1,
+ Nu = 256,
+};
+
+static uchar*
+repparse1(uchar *d, uchar *e, int g[], int l[], int c,
+ void (*f)(int t, int v, int g[], int l[], int c, void *a), void *a)
+{
+ int z, k, t, v, i;
+
+ while(d < e){
+ v = 0;
+ t = *d++;
+ z = t & 3, t >>= 2;
+ k = t & 3, t >>= 2;
+ switch(z){
+ case 3:
+ d += 4;
+ if(d > e) continue;
+ v = d[-4] | d[-3]<<8 | d[-2]<<16 | d[-1]<<24;
+ break;
+ case 2:
+ d += 2;
+ if(d > e) continue;
+ v = d[-2] | d[-1]<<8;
+ break;
+ case 1:
+ d++;
+ if(d > e) continue;
+ v = d[-1];
+ break;
+ }
+ switch(k){
+ case 0: /* main item*/
+ switch(t){
+ case Collection:
+ memset(l, 0, Nl*sizeof(l[0]));
+ i = l[Nu] | g[UsagPg]<<16;
+ l[Usage] = i;
+ (*f)(t, v, g, l, c, a);
+
+ d = repparse1(d, e, g, l, v, f, a);
+
+ l[Usage] = i;
+ (*f)(CollectionEnd, v, g, l, c, a);
+ continue;
+ case CollectionEnd:
+ return d;
+ case Input:
+ case Output:
+ case Feature:
+ if(l[UsgCnt] == 0 && l[UsagMin] != 0 && l[UsagMin] < l[UsagMax])
+ for(i=l[UsagMin]; i<=l[UsagMax] && l[UsgCnt] < Nu; i++)
+ l[Nl + l[UsgCnt]++] = i;
+ for(i=0; i<g[RepCnt]; i++){
+ l[Usage] = i < l[UsgCnt] ? l[Nl + i] : 0;
+ (*f)(t, v, g, l, c, a);
+ }
+ break;
+ }
+ memset(l, 0, Nl*sizeof(l[0]));
+ continue;
+ case 1: /* global item */
+ if(t == Push){
+ int w[Ng];
+ memmove(w, g, sizeof(w));
+ d = repparse1(d, e, w, l, c, f, a);
+ } else if(t == Pop){
+ return d;
+ } else if(t < Ng){
+ if(t == RepId)
+ v &= 0xFF;
+ else if(t == UsagPg)
+ v &= 0xFFFF;
+ else if(t != RepSize && t != RepCnt){
+ v = signext(v, (z == 3) ? 32 : 8*z);
+ }
+ g[t] = v;
+ }
+ continue;
+ case 2: /* local item */
+ if(l[Delim] != 0)
+ continue;
+ if(t == Delim){
+ l[Delim] = 1;
+ } else if(t < Delim){
+ if(z != 3 && (t == Usage || t == UsagMin || t == UsagMax))
+ v = (v & 0xFFFF) | (g[UsagPg] << 16);
+ l[t] = v;
+ if(t == Usage && l[UsgCnt] < Nu)
+ l[Nl + l[UsgCnt]++] = v;
+ }
+ continue;
+ case 3: /* long item */
+ if(t == 15)
+ d += v & 0xFF;
+ continue;
+ }
+ }
+ return d;
+}
+
+/*
+ * parse the report descriptor and call f for every (Input, Output
+ * and Feature) main item as often as it would appear in the report
+ * data packet.
+ */
+static void
+repparse(uchar *d, uchar *e,
+ void (*f)(int t, int v, int g[], int l[], int c, void *a), void *a)
+{
+ int l[Nl+Nu], g[Ng];
+
+ memset(l, 0, sizeof(l));
+ memset(g, 0, sizeof(g));
+ repparse1(d, e, g, l, 0, f, a);
+}
+
+static int
+setproto(Hiddev *f, Iface *iface)
+{
+ int proto;
+
+ usbcmd(f->dev, Rh2d|Rclass|Riface, Setidle, f->idle<<8, iface->id, nil, 0);
+
+ f->nrep = usbcmd(f->dev, Rd2h|Rstd|Riface, Rgetdesc, Dreport<<8, iface->id,
+ f->rep, sizeof(f->rep));
+ if(f->nrep > 0){
+ if(debug){
+ int i;
+
+ fprint(2, "report descriptor:");
+ for(i = 0; i < f->nrep; i++){
+ if(i%8 == 0)
+ fprint(2, "\n\t");
+ fprint(2, "%#2.2ux ", f->rep[i]);
+ }
+ fprint(2, "\n");
+ }
+ proto = Reportproto;
+ } else {
+ switch(iface->csp){
+ case KbdCSP:
+ f->nrep = sizeof(kbdbootrep);
+ memmove(f->rep, kbdbootrep, f->nrep);
+ break;
+ case PtrCSP:
+ f->nrep = sizeof(ptrbootrep);
+ memmove(f->rep, ptrbootrep, f->nrep);
+ break;
+ default:
+ werrstr("no report descriptor");
+ return -1;
+ }
+ proto = Bootproto;
+ }
+
+ /*
+ * if a HID's subclass code is 1 (boot mode), it will support
+ * setproto, otherwise it is not guaranteed to.
+ */
+ if(Subclass(iface->csp) != 1)
+ return 0;
+
+ return usbcmd(f->dev, Rh2d|Rclass|Riface, Setproto, proto, iface->id, nil, 0);
+}
+
+static void
+hdfree(Hiddev *f)
+{
+ if(f->kinfd >= 0)
+ close(f->kinfd);
+ if(f->minfd >= 0)
+ close(f->minfd);
+ if(f->ep != nil)
+ closedev(f->ep);
+ if(f->dev != nil)
+ closedev(f->dev);
+ free(f);
+}
+
+static void
+hdfatal(Hiddev *f, char *sts)
+{
+ if(sts != nil)
+ fprint(2, "%s: fatal: %s\n", argv0, sts);
+ else
+ fprint(2, "%s: exiting\n", argv0);
+ if(f->repeatc != nil)
+ sendul(f->repeatc, Diemsg);
+ hdfree(f);
+ threadexits(sts);
+}
+
+static void
+putscan(Hiddev *f, uchar sc, uchar up)
+{
+ uchar s[2] = {SCesc1, 0};
+
+ if(sc == 0)
+ return;
+ s[1] = up | sc&Keymask;
+ if(isext(sc))
+ write(f->kinfd, s, 2);
+ else
+ write(f->kinfd, s+1, 1);
+}
+
+static void
+sleepproc(void* a)
+{
+ Channel *c = a;
+ int ms;
+
+ threadsetname("sleepproc");
+ while((ms = recvul(c)) > 0)
+ sleep(ms);
+ chanfree(c);
+}
+
+static void
+repeatproc(void* arg)
+{
+ Hiddev *f = arg;
+ Channel *repeatc, *sleepc;
+ ulong l, t;
+ uchar sc;
+ Alt a[3];
+
+ repeatc = f->repeatc;
+ threadsetname("repeatproc");
+
+ sleepc = chancreate(sizeof(ulong), 0);
+ if(sleepc != nil)
+ proccreate(sleepproc, sleepc, Stack);
+
+ a[0].c = repeatc;
+ a[0].v = &l;
+ a[0].op = CHANRCV;
+ a[1].c = sleepc;
+ a[1].v = &t;
+ a[1].op = sleepc!=nil ? CHANSND : CHANNOP;
+ a[2].c = nil;
+ a[2].v = nil;
+ a[2].op = CHANEND;
+
+ l = Awakemsg;
+ while(l != Diemsg){
+ if(l == Awakemsg){
+ l = recvul(repeatc);
+ continue;
+ }
+ sc = l & 0xff;
+ t = kbdelay;
+ if(alt(a) == 1){
+ t = kbrepeat;
+ while(alt(a) == 1)
+ putscan(f, sc, 0);
+ }
+ }
+ if(sleepc != nil)
+ sendul(sleepc, 0);
+ chanfree(repeatc);
+ threadexits(nil);
+}
+
+static void
+stoprepeat(Hiddev *f)
+{
+ sendul(f->repeatc, Awakemsg);
+}
+
+static void
+startrepeat(Hiddev *f, uchar sc)
+{
+ sendul(f->repeatc, sc);
+}
+
+static void
+hidparse(int t, int f, int g[], int l[], int, void *a)
+{
+ Hidreport *p = a;
+ Hidslot *s = &p->s[p->ns];
+ int v, m, cp;
+
+ switch(t){
+ case Input:
+ if(g[RepId] != 0){
+ if(p->p[0] != g[RepId]){
+ p->o = 0;
+ return;
+ }
+ if(p->o < 8)
+ p->o = 8; /* skip report id byte */
+ }
+ break;
+ case Collection:
+ if(g[RepId] != 0 && p->p[0] != g[RepId])
+ return;
+ if(s->valid && p->ns < nelem(p->s)-1)
+ s = &p->s[++p->ns];
+ memset(s, 0, sizeof(*s));
+ s->usage = l[Usage];
+ s->id = (p->ns+1)<<8 | g[RepId];
+ return;
+ case CollectionEnd:
+ if(g[RepId] != 0 && p->p[0] != g[RepId])
+ return;
+ if(!s->valid || s->usage != l[Usage])
+ return;
+ /* if out of range or touchscreen finger not touching, ignore the slot */
+ if(s->oor || s->usage == 0x0D0022 && s->b == 0)
+ s->valid = 0;
+ return;
+ default:
+ return;
+ }
+ v = getbits(p->p, p->e, g[RepSize], p->o);
+ p->o += g[RepSize];
+
+ if((f & (Fconst|Fdata)) != Fdata)
+ return;
+
+ if(debug > 1)
+ fprint(2, "hidparse: t=%x f=%x usage=%x v=%x\n", t, f, l[Usage], v);
+
+ if((cp = ((l[Usage]>>16) == 0x0c)) || (l[Usage]>>16) == 0x07){ /* consumer/keycode */
+ if((f & (Fvar|Farray)) == Fvar)
+ if(v != 0) v = l[Usage] & 0xFF;
+ if(p->nk < nelem(p->k) && v != 0)
+ p->k[p->nk++] = v | cp*Consumer;
+ return;
+ }
+
+ if(g[LogiMin] < 0)
+ v = signext(v, g[RepSize]);
+ if((f & (Fvar|Farray)) == Fvar && v >= g[LogiMin] && v <= g[LogiMax]){
+ /*
+ * we use logical units below, but need the
+ * sign to be correct for mouse deltas.
+ * so if physical unit is signed but logical
+ * is unsigned, convert to signed but in logical
+ * units.
+ */
+ if((f & (Fabs|Frel)) == Frel
+ && g[PhysMin] < 0 && g[PhysMax] > 0
+ && g[LogiMin] >= 0 && g[LogiMin] < g[LogiMax])
+ v -= (g[PhysMax] * (g[LogiMax] - g[LogiMin])) / (g[PhysMax] - g[PhysMin]);
+
+ switch(l[Usage]){
+ default:
+ return;
+
+ case 0x090001:
+ case 0x090002:
+ case 0x090003:
+ case 0x090004:
+ case 0x090005:
+ case 0x090006:
+ case 0x090007:
+ case 0x090008:
+ m = 1<<(l[Usage] - 0x090001);
+ Button:
+ s->m |= m;
+ s->b &= ~m;
+ if(v != 0)
+ s->b |= m;
+ break;
+
+ case 0x0D0032: /* In Range */
+ s->oor = !v;
+ break;
+
+ case 0x0D0042: /* Tip Switch */
+ m = 1;
+ goto Button;
+ case 0x0D0044: /* Barrel Switch */
+ m = 2;
+ goto Button;
+ case 0x0D0045: /* Eraser */
+ m = 4;
+ goto Button;
+
+ case 0x0D0048: /* Contact width */
+ s->w = v;
+ break;
+ case 0x0D0049: /* Contact height */
+ s->h = v;
+ break;
+
+ case 0x0D0051: /* Contact identifier */
+ s->id = v;
+ break;
+
+ case 0x010030:
+ if((f & (Fabs|Frel)) == Fabs){
+ v = ((vlong)(v - g[LogiMin]) << 31) / (g[LogiMax] - g[LogiMin]);
+ s->abs |= 1;
+ }
+ s->x = v;
+ break;
+ case 0x010031:
+ if((f & (Fabs|Frel)) == Fabs){
+ v = ((vlong)(v - g[LogiMin]) << 31) / (g[LogiMax] - g[LogiMin]);
+ s->abs |= 2;
+ }
+ s->y = v;
+ break;
+ case 0x010038:
+ if((f & (Fabs|Frel)) == Fabs)
+ s->abs |= 4;
+ s->z = v;
+ break;
+ }
+ s->valid = 1;
+ }
+}
+
+static void
+sethipri(void)
+{
+ char fn[64];
+ int fd;
+
+ snprint(fn, sizeof(fn), "/proc/%d/ctl", getpid());
+ fd = open(fn, OWRITE);
+ if(fd < 0)
+ return;
+ fprint(fd, "pri 13");
+ close(fd);
+}
+
+static ushort *
+keykey(ushort *a, ushort k, int n)
+{
+ while(n > 0){
+ if(*a++ == k)
+ return a-1;
+ n--;
+ }
+ return nil;
+}
+
+static void
+readerproc(void* a)
+{
+ char err[ERRMAX], mbuf[80];
+ ushort lastk[Nkey], uks[Nkey], dks[Nkey], nuks, ndks;
+ int i, c, bpress, lastb, nlastk, stopped;
+ int abs, x, y, z, b;
+ Hidreport p;
+ Hidslot lasts[nelem(p.s)], *s, *l;
+ Hiddev *f = a;
+
+ threadsetname("readerproc %s", f->ep->dir);
+ sethipri();
+
+ memset(&p, 0, sizeof(p));
+ memset(lasts, 0, sizeof(lasts));
+ lastb = nlastk = 0;
+
+ for(;;){
+ if(f->ep == nil)
+ hdfatal(f, nil);
+ if(f->ep->maxpkt < 1 || f->ep->maxpkt > sizeof(p.p))
+ hdfatal(f, "hid: weird maxpkt");
+
+ memset(p.p, 0, sizeof(p.p));
+ c = read(f->ep->dfd, p.p, f->ep->maxpkt);
+ if(c <= 0){
+ if(c < 0)
+ rerrstr(err, sizeof(err));
+ else
+ strcpy(err, "zero read");
+
+ fprint(2, "%s: hid: %s: read: %s\n", argv0, f->ep->dir, err);
+
+ /* reset the whole device */
+ devctl(f->dev, "reset");
+
+ hdfatal(f, err);
+ }
+
+ p.o = 0;
+ p.e = p.p + c;
+ p.ns = 0;
+ memset(p.s, 0, sizeof(p.s[0]));
+ repparse(f->rep, f->rep+f->nrep, hidparse, &p);
+ if(p.s[p.ns].valid)
+ p.ns++;
+
+ /* handle keyboard report */
+ if(p.nk != 0 || nlastk != 0){
+ if(debug){
+ fprint(2, "kbd: ");
+ for(i = 0; i < p.nk; i++)
+ fprint(2, "%#4.4ux ", p.k[i]);
+ fprint(2, "\n");
+ }
+
+ if(f->kinfd < 0){
+ f->kinfd = open("/dev/kbin", OWRITE);
+ if(f->kinfd < 0)
+ hdfatal(f, "open /dev/kbin");
+
+ f->repeatc = chancreate(sizeof(ulong), 0);
+ if(f->repeatc == nil)
+ hdfatal(f, "chancreate failed");
+ proccreate(repeatproc, f, Stack);
+ }
+
+ /* collect key presses/releases */
+ for(i=nuks=0; i<nlastk; i++){
+ if(keykey(p.k, lastk[i], p.nk) == nil)
+ uks[nuks++] = sctab[lastk[i]];
+ }
+ for(i=ndks=0; i<p.nk; i++){
+ if(keykey(lastk, p.k[i], nlastk) == nil)
+ dks[ndks++] = sctab[p.k[i]];
+ }
+
+ /*
+ * stop the repeats first to avoid race condition when
+ * the key is released but the repeat happens right after
+ */
+ for(stopped = i = 0; i < nuks; i++){
+ if(ndks == 0 || keykey(dks, uks[i], ndks) != nil){
+ stoprepeat(f);
+ stopped = 1;
+ break;
+ }
+ }
+ for(i = 0; i < nuks; i++)
+ putscan(f, uks[i], Keyup);
+ for(i = 0; i < ndks; i++)
+ putscan(f, dks[i], 0);
+
+ if(stopped == 0 && ndks > 0)
+ startrepeat(f, dks[ndks-1]);
+
+ memmove(lastk, p.k, (nlastk = p.nk)*sizeof(lastk[0]));
+ p.nk = 0;
+ }
+
+ /* handle mouse/touchpad */
+ if(p.ns == 0)
+ continue;
+
+ /* combine all the slots */
+ bpress = abs = x = y = z = b = 0;
+ for(i=0; i<p.ns; *l = *s, i++){
+ s = &p.s[i];
+
+ /* find the last slot of the same id */
+ for(l = lasts; l->valid && l < &lasts[nelem(lasts)-1]; l++)
+ if(l->usage == s->usage && l->id == s->id)
+ break;
+ if(l == &lasts[nelem(lasts)-1] || !l->valid)
+ *l = *s;
+
+ /* convert absolute z to relative */
+ z += s->z;
+ if(s->abs & 4)
+ z -= l->z;
+
+ if(debug) {
+ if((s->abs & 3) == 3)
+ fprint(2, "ptr[%d]: id=%x b=%x m=%x x=%f y=%f z=%d\n",
+ i, s->id, s->b, s->m,
+ (uint)s->x / 2147483648.0,
+ (uint)s->y / 2147483648.0,
+ s->z);
+ else
+ fprint(2, "ptr[%d]: id=%x b=%x m=%x x=%d y=%d z=%d\n",
+ i, s->id, s->b, s->m, s->x, s->y, s->z);
+ }
+
+ /* map to mouse buttons */
+ b |= s->b & 1;
+ if(s->b & (4|8))
+ b |= 2;
+ if(s->b & 2)
+ b |= 4;
+ bpress |= s->m;
+
+ /* X/Y are absolute? */
+ if((s->abs & 3) == 3){
+ /* ignore absolute position when nothing changed */
+ if(s->abs == l->abs && s->x == l->x && s->y == l->y && s->b == l->b)
+ continue;
+ abs = 1;
+ if(f->dev->usb->vid == 0x28bd && f->dev->usb->did == 0x0920){
+ x = s->y;
+ y = 0x80000000UL - s->x;
+ }else{
+ x = s->x;
+ y = s->y;
+ }
+ } else {
+ /* everything needs to be relative */
+ if((s->abs & 3) != 0 || abs)
+ continue;
+ x += s->x;
+ y += s->y;
+ }
+ }
+
+ if(bpress == 0)
+ b = lastb & 7;
+ if(z != 0)
+ b |= z > 0 ? 8 : 16;
+
+ if(abs || x != 0 || y != 0 || z != 0 || b != lastb){
+ lastb = b;
+
+ if(f->minfd < 0){
+ f->minfd = open("/dev/mousein", OWRITE);
+ if(f->minfd < 0)
+ hdfatal(f, "open /dev/mousein");
+ }
+ seprint(mbuf, mbuf+sizeof(mbuf), "%c%11d %11d %11d", "ma"[abs], x, y, b);
+ write(f->minfd, mbuf, strlen(mbuf));
+ }
+ }
+}
+
+static void
+gaomons620(Hiddev *f)
+{
+ static uchar descr[] = {
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x01, /* Usage (Digitizer), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x08, /* Report ID (8), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xA0, /* Collection (Physical), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x45, /* Usage (Eraser), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+
+ 0xA4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x34, /* Physical Minimum (0), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x27, 0x00, 0x80, 0x00, 0x00, /* Logical Maximum, */
+ 0x47, 0x00, 0x80, 0x00, 0x00, /* Physical Maximum, */
+ 0x81, 0x02, /* Input (Variable), */
+
+ 0x09, 0x31, /* Usage (Y), */
+ 0x27, 0x00, 0x50, 0x00, 0x00, /* Logical Maximum, */
+ 0x47, 0x00, 0x50, 0x00, 0x00, /* Physical Maximum, */
+ 0x81, 0x02, /* Input (Variable), */
+
+ 0xB4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x27, 0x00, 0x00, 0x01, 0x00, /* Logical Maximum, */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+ };
+ static int idx[] = { 0x64, 0x65, 0x6e, 0x79, 0x7a, 0x7b, 0xc8, 0xc9 };
+ Dev *d = f->dev;
+ int i;
+
+ /* we have to fetch a bunch of strings to switch to non-phone mode */
+ for(i=0; i < nelem(idx); i++)
+ free(loaddevstr(d, idx[i]));
+
+ /* override report descriptor */
+ memmove(f->rep, descr, f->nrep = sizeof(descr));
+}
+
+static void
+quirks(Hiddev *f)
+{
+ Dev *d;
+
+ d = f->dev;
+
+ if(d->usb->vid == 0x256c && d->usb->did == 0x006d){
+ gaomons620(f);
+ return;
+ }
+
+ /* Elecom trackball report descriptor lies by
+ * omission, failing to mention all its buttons.
+ * We patch the descriptor with a correct count
+ * which lets us parse full reports. Tested with:
+ * Elecom HUGE (M-HT1DRBK, M-HT1URBK) */
+ if(d->usb->vid == 0x056e && d->usb->did == 0x010c){
+ if(f->nrep < 32
+ || f->rep[12] != 0x95
+ || f->rep[14] != 0x75
+ || f->rep[15] != 0x01
+ || f->rep[20] != 0x29
+ || f->rep[30] != 0x75)
+ return;
+
+ f->rep[13] = 8;
+ f->rep[21] = 8;
+ f->rep[31] = 0;
+ return;
+ }
+}
+
+static int
+hdsetup(Dev *d, Ep *ep)
+{
+ Hiddev *f;
+ uchar desc[512];
+ int n;
+
+ f = emallocz(sizeof(Hiddev), 1);
+ f->minfd = -1;
+ f->kinfd = -1;
+ incref(d);
+ f->dev = d;
+ /*
+ * DWC OTG controller misses some split transaction inputs.
+ * Set nonzero idle time to return more frequent reports
+ * of keyboard state, to avoid losing key up/down events.
+ */
+ n = read(d->cfd, desc, sizeof(desc) - 1);
+ if(n > 0){
+ desc[n] = 0;
+ if(strstr((char*)desc, "dwcotg") != nil)
+ f->idle = Dwcidle;
+ }
+ if(setproto(f, ep->iface) < 0){
+ fprint(2, "%s: %s: setproto: %r\n", argv0, d->dir);
+ goto Err;
+ }
+ f->ep = openep(f->dev, ep);
+ if(f->ep == nil){
+ fprint(2, "%s: %s: openep %d: %r\n", argv0, d->dir, ep->id);
+ goto Err;
+ }
+ if(opendevdata(f->ep, OREAD) < 0){
+ fprint(2, "%s: %s: opendevdata: %r\n", argv0, f->ep->dir);
+ goto Err;
+ }
+ quirks(f);
+ procrfork(readerproc, f, Stack, RFNOTEG);
+ return 0;
+Err:
+ hdfree(f);
+ return -1;
+}
+
+static void
+fsread(Req *r)
+{
+ char msg[48], *s, *e;
+
+ s = msg;
+ e = msg+sizeof(msg);
+ *s = 0;
+ if(havekbd)
+ e = seprint(s, e, "repeat %d\ndelay %d\n", kbrepeat, kbdelay);
+ USED(e);
+ readstr(r, msg);
+ respond(r, nil);
+}
+
+static void
+fswrite(Req *r)
+{
+ char msg[256], *f[4];
+ void *data;
+ int nf, sz;
+ Dev *dev;
+
+ dev = r->fid->file->aux;
+ data = r->ifcall.data;
+ sz = r->ifcall.count;
+ if(r->fid->aux == (void*)Rawon){
+ if(sz == 6 && memcmp(data, "rawoff", 6) == 0)
+ r->fid->aux = nil;
+ else if(usbcmd(dev, Rh2d|Rclass|Riface, Setreport, Reportout, 0, data, sz) < 0){
+ responderror(r);
+ return;
+ }
+ }else{
+ snprint(msg, sizeof(msg), "%.*s", utfnlen(data, sz), data);
+ nf = tokenize(msg, f, nelem(f));
+ if(nf == 1 && strcmp(f[0], "rawon") == 0)
+ r->fid->aux = (void*)Rawon;
+ else if(nf == 2 && strcmp(f[0], "repeat") == 0)
+ kbrepeat = atoi(f[1]);
+ else if(nf == 2 && strcmp(f[0], "delay") == 0)
+ kbdelay = atoi(f[1]);
+ else if(nf == 2 && strcmp(f[0], "debug") == 0)
+ debug = atoi(f[1]);
+ else{
+ respond(r, "invalid ctl message");
+ return;
+ }
+ }
+ r->ofcall.count = sz;
+ respond(r, nil);
+}
+
+static Srv fs = {
+ .read = fsread,
+ .write = fswrite,
+};
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-d] devid\n", argv0);
+ threadexits("usage");
+}
+
+void
+threadmain(int argc, char* argv[])
+{
+ int i, n;
+ Dev *d;
+ Ep *ep;
+ Usbdev *ud;
+ char buf[32];
+
+ ARGBEGIN{
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage();
+ }ARGEND;
+ if(argc != 1)
+ usage();
+ d = getdev(*argv);
+ if(d == nil)
+ sysfatal("getdev: %r");
+ ud = d->usb;
+ for(i = n = 0; i < nelem(ud->ep); i++){
+ if((ep = ud->ep[i]) == nil)
+ continue;
+ if(ep->type != Eintr || ep->dir != Ein)
+ continue;
+ switch(ep->iface->csp){
+ case KbdCSP:
+ havekbd = 1;
+ case PtrCSP:
+ case PtrNonBootCSP:
+ case HidCSP:
+ n += hdsetup(d, ep) == 0;
+ break;
+ }
+ }
+ closedev(d);
+ if(n > 0){
+ fs.tree = alloctree(user, "usb", DMDIR|0555, nil);
+ snprint(buf, sizeof buf, "hidU%sctl", d->hname);
+ createfile(fs.tree->root, buf, user, 0666, d);
+ snprint(buf, sizeof buf, "%s.hid", d->hname);
+ threadpostsharesrv(&fs, nil, "usb", buf);
+ }
+ threadexits(nil);
+}