summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2012-12-19 12:09:35 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2012-12-19 12:09:35 +0100
commit7b34e8e75910cacfa82bcbcdd488a719f4ca8efd (patch)
tree3229965da2dfb97437635c529b72e741ec21a26d
parent30b8fa0eef5bb5e12a3d84af9b957c89905c5147 (diff)
downloadplan9front-7b34e8e75910cacfa82bcbcdd488a719f4ca8efd.tar.xz
nusb/kb: use report protocol for mouse
instead of forcing mouse to boot protocol, which often doesnt work, we set it to report protocol and parse the hid report descriptor. if thers no such descriptor we revert to boot protocol. all mouse packet parsing is done by report parser, even for boot protocol. also all the work arrounds for the leadbyte hack (report id?) are removed. keyboards should not be affected by this change.
-rw-r--r--sys/src/cmd/nusb/kb/hid.h54
-rw-r--r--sys/src/cmd/nusb/kb/kb.c400
2 files changed, 360 insertions, 94 deletions
diff --git a/sys/src/cmd/nusb/kb/hid.h b/sys/src/cmd/nusb/kb/hid.h
index dba2b027d..0456da61d 100644
--- a/sys/src/cmd/nusb/kb/hid.h
+++ b/sys/src/cmd/nusb/kb/hid.h
@@ -23,6 +23,58 @@ enum {
Reportout = 0x0200,
};
+/*
+ * USB HID report descriptor item tags
+ */
+enum {
+ /* main items */
+ Input = 8,
+ Output,
+ Collection,
+ Feature,
+
+ CollectionEnd,
+
+ /* global items */
+ UsagPg = 0,
+ LogiMin,
+ LogiMax,
+ PhysMin,
+ PhysMax,
+ UnitExp,
+ UnitTyp,
+ RepSize,
+ RepId,
+ RepCnt,
+
+ Push,
+ Pop,
+
+ /* local items */
+ Usage = 0,
+ UsagMin,
+ UsagMax,
+ DesgIdx,
+ DesgMin,
+ DesgMax,
+ StrgIdx,
+ StrgMin,
+ StrgMax,
+
+ Delim,
+};
+
+/* main item flags */
+enum {
+ Fdata = 0<<0, Fconst = 1<<0,
+ Farray = 0<<1, Fvar = 1<<1,
+ Fabs = 0<<2, Frel = 1<<2,
+ Fnowrap = 0<<3, Fwrap = 1<<3,
+ Flinear = 0<<4, Fnonlin = 1<<4,
+ Fpref = 0<<5, Fnopref = 1<<5,
+ Fnonull = 0<<6, Fnullst = 1<<6,
+};
+
enum {
/* keyboard modifier bits */
Mlctrl = 0,
@@ -61,5 +113,3 @@ enum {
Keyup = 0x80, /* flag bit */
Keymask = 0x7f, /* regular scan code bits */
};
-
-int kbmain(Dev *d, int argc, char*argv[]);
diff --git a/sys/src/cmd/nusb/kb/kb.c b/sys/src/cmd/nusb/kb/kb.c
index be96f9624..d819d41ba 100644
--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -35,7 +35,11 @@ struct KDev
Dev* dev; /* usb device*/
Dev* ep; /* endpoint to get events */
int infd; /* used to send events to kernel */
- Channel*repeatc; /* only for keyboard */
+ Channel *repeatc; /* only for keyboard */
+
+ /* report descriptor */
+ int nrep;
+ uchar rep[128];
};
/*
@@ -92,17 +96,199 @@ static char sctab[256] =
[0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
-static int kbdebug;
-static int accel;
+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;
+
+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]));
+ d = repparse1(d, e, g, l, v, f, 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++){
+ if(i < l[UsgCnt])
+ l[Usage] = l[Nl + i];
+ (*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
-setbootproto(KDev* f, int eid)
+setproto(KDev *f, int eid)
{
- int r, id;
+ int id, proto;
- r = Rh2d|Rclass|Riface;
+ proto = Bootproto;
id = f->dev->usb->ep[eid]->iface->id;
- return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
+ if(f->dev->usb->ep[eid]->iface->csp == PtrCSP){
+ f->nrep = usbcmd(f->dev, Rd2h|Rstd|Riface, Rgetdesc, Dreport<<8, 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 {
+ f->nrep = sizeof(ptrbootrep);
+ memmove(f->rep, ptrbootrep, f->nrep);
+ }
+ }
+ return usbcmd(f->dev, Rh2d|Rclass|Riface, Setproto, proto, id, nil, 0);
}
static int
@@ -125,7 +311,7 @@ recoverkb(KDev *f)
for(i = 0; i < 10; i++){
sleep(500);
if(opendevdata(f->dev, ORDWR) >= 0){
- setbootproto(f, f->ep->id);
+ setproto(f, f->ep->id);
break;
}
/* else usbd still working... */
@@ -179,69 +365,135 @@ sethipri(void)
close(fd);
}
-static int
-scale(int x)
+typedef struct Ptr Ptr;
+struct Ptr
{
- int sign = 1;
+ int x;
+ int y;
+ int z;
- if(x < 0){
- sign = -1;
- x = -x;
- }
- switch(x){
- case 0:
- case 1:
- case 2:
- case 3:
- break;
- case 4:
- x = 6 + (accel>>2);
- break;
- case 5:
- x = 9 + (accel>>1);
- break;
- default:
- x *= MaxAcc;
- break;
- }
- return sign*x;
-}
+ int b;
+
+ int absx;
+ int absy;
+ int absz;
+
+ int o;
+ uchar *e;
+ uchar p[128];
+};
-static short
-s16(void *p)
+static void
+ptrparse(int t, int f, int g[], int l[], int, void *a)
{
- uchar *b = p;
- return b[0] | b[1]<<8;
+ int v, m;
+ Ptr *p = a;
+
+ if(t != Input)
+ return;
+ 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 */
+ }
+ v = getbits(p->p, p->e, g[RepSize], p->o);
+ 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]){
+ case 0x090001:
+ m = 1;
+ goto Button;
+ case 0x090002:
+ m = 4;
+ goto Button;
+ case 0x090003:
+ m = 2;
+ Button:
+ p->b &= ~m;
+ if(v != 0)
+ p->b |= m;
+ break;
+ case 0x010030:
+ if((f & (Fabs|Frel)) == Fabs){
+ p->x = (v - p->absx);
+ p->absx = v;
+ } else {
+ p->x = v;
+ p->absx += v;
+ }
+ break;
+ case 0x010031:
+ if((f & (Fabs|Frel)) == Fabs){
+ p->y = (v - p->absy);
+ p->absy = v;
+ } else {
+ p->y = v;
+ p->absy += v;
+ }
+ break;
+ case 0x010038:
+ if((f & (Fabs|Frel)) == Fabs){
+ p->z = (v - p->absz);
+ p->absz = v;
+ } else {
+ p->z = v;
+ p->absz += v;
+ }
+ p->b &= ~(8|16);
+ if(p->z != 0)
+ p->b |= (p->z > 0) ? 8 : 16;
+ break;
+ }
+ }
+ p->o += g[RepSize];
}
static void
ptrwork(void* a)
{
- static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
- int x, y, z, b, c, nerrs, skiplead;
- char err[ERRMAX], buf[64];
+ char err[ERRMAX];
char mbuf[80];
+ int c, nerrs;
KDev* f = a;
+ Ptr p;
- kbprocname(f, "ptrwork");
+ kbprocname(f, "ptr");
sethipri();
- skiplead = -1;
+ memset(&p, 0, sizeof(p));
+
nerrs = 0;
for(;;){
if(f->ep == nil)
kbfatal(f, nil);
- if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
- kbfatal(f, "mouse: weird mouse maxpkt");
- memset(buf, 0, sizeof buf);
- c = read(f->ep->dfd, buf, f->ep->maxpkt);
+ if(f->ep->maxpkt < 1 || f->ep->maxpkt > sizeof(p.p))
+ kbfatal(f, "ptr: weird mouse 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");
if(++nerrs < 3){
- fprint(2, "%s: mouse: %s: read: %s\n", argv0, f->ep->dir, err);
+ fprint(2, "%s: ptr: %s: read: %s\n", argv0, f->ep->dir, err);
if(strstr(err, "babble") != 0)
recoverkb(f);
continue;
@@ -249,47 +501,12 @@ ptrwork(void* a)
kbfatal(f, err);
}
nerrs = 0;
- if(c < 3)
- continue;
- if(c > 4){
- /*
- * horrible horrible hack. some mouse send a
- * constant 0x01 lead byte. stop the hack as
- * soon as buf[0] changes.
- */
- if(skiplead == -1)
- skiplead = buf[0] & 0xFF;
- if(skiplead == 0x01 && skiplead == buf[0])
- memmove(buf, buf+1, --c);
- else
- skiplead = 0;
- }
+ p.o = 0;
+ p.e = p.p + c;
+ repparse(f->rep, f->rep+f->nrep, ptrparse, &p);
- z = 0;
- if(c == 6 && f->dev->usb->vid == 0x1a7c){
- /* Evoluent vertical mouse */
- x = s16(&buf[1]);
- y = s16(&buf[3]);
- z = buf[5];
- } else {
- x = buf[1];
- y = buf[2];
- if(c > 3)
- z = buf[3];
- }
- if(accel){
- x = scale(x);
- y = scale(y);
- }
- b = maptab[buf[0] & 0x7];
- if(z > 0) /* up */
- b |= 0x08;
- if(z < 0) /* down */
- b |= 0x10;
- if(kbdebug > 1)
- fprint(2, "%s: m%11d %11d %11d\n", argv0, x, y, b);
- seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
+ seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", p.x, p.y, p.b);
if(write(f->infd, mbuf, strlen(mbuf)) < 0)
kbfatal(f, "mousein i/o");
}
@@ -479,7 +696,7 @@ kbdwork(void *a)
char err[128];
KDev *f = a;
- kbprocname(f, "kbdwork");
+ kbprocname(f, "kbd");
f->repeatc = chancreate(sizeof(ulong), 0);
if(f->repeatc == nil)
@@ -516,7 +733,7 @@ kbdwork(void *a)
continue;
if(kbdbusy(buf + 2, c - 2))
continue;
- if(usbdebug > 2 || kbdebug > 1){
+ if(usbdebug > 2 || debug > 1){
fprint(2, "kbd mod %x: ", buf[0]);
for(i = 2; i < c; i++)
fprint(2, "kc %x ", buf[i]);
@@ -540,8 +757,8 @@ kbstart(Dev *d, Ep *ep, char *infile, void (*f)(void*))
}
incref(d);
kd->dev = d;
- if(setbootproto(kd, ep->id) < 0){
- fprint(2, "%s: %s: bootproto: %r\n", argv0, d->dir);
+ if(setproto(kd, ep->id) < 0){
+ fprint(2, "%s: %s: setproto: %r\n", argv0, d->dir);
goto Err;
}
kd->ep = openep(kd->dev, ep->id);
@@ -576,10 +793,9 @@ threadmain(int argc, char* argv[])
ARGBEGIN{
case 'a':
- accel = strtol(EARGF(usage()), nil, 0);
break;
case 'd':
- kbdebug++;
+ debug++;
break;
default:
usage();