shithub: fork

Download patch

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

diff: cannot open b/sys/src/cmd/nusb/kb//null: file does not exist: 'b/sys/src/cmd/nusb/kb//null' diff: cannot open b/sys/src/cmd/nusb//null: file does not exist: 'b/sys/src/cmd/nusb//null'
--- /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);
+}