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.
This commit is contained in:
cinap_lenrek 2012-12-19 12:09:35 +01:00
parent 30b8fa0eef
commit 7b34e8e759
2 changed files with 360 additions and 94 deletions

View file

@ -23,6 +23,58 @@ enum {
Reportout = 0x0200, 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 { enum {
/* keyboard modifier bits */ /* keyboard modifier bits */
Mlctrl = 0, Mlctrl = 0,
@ -61,5 +113,3 @@ enum {
Keyup = 0x80, /* flag bit */ Keyup = 0x80, /* flag bit */
Keymask = 0x7f, /* regular scan code bits */ Keymask = 0x7f, /* regular scan code bits */
}; };
int kbmain(Dev *d, int argc, char*argv[]);

View file

@ -35,7 +35,11 @@ struct KDev
Dev* dev; /* usb device*/ Dev* dev; /* usb device*/
Dev* ep; /* endpoint to get events */ Dev* ep; /* endpoint to get events */
int infd; /* used to send events to kernel */ 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, [0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
}; };
static int kbdebug; static uchar ptrbootrep[] = {
static int accel; 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 static int
setbootproto(KDev* f, int eid) signext(int v, int bits)
{ {
int r, id; int s;
r = Rh2d|Rclass|Riface; 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
setproto(KDev *f, int eid)
{
int id, proto;
proto = Bootproto;
id = f->dev->usb->ep[eid]->iface->id; 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 static int
@ -125,7 +311,7 @@ recoverkb(KDev *f)
for(i = 0; i < 10; i++){ for(i = 0; i < 10; i++){
sleep(500); sleep(500);
if(opendevdata(f->dev, ORDWR) >= 0){ if(opendevdata(f->dev, ORDWR) >= 0){
setbootproto(f, f->ep->id); setproto(f, f->ep->id);
break; break;
} }
/* else usbd still working... */ /* else usbd still working... */
@ -179,69 +365,135 @@ sethipri(void)
close(fd); close(fd);
} }
static int typedef struct Ptr Ptr;
scale(int x) struct Ptr
{ {
int sign = 1; int x;
int y;
int z;
if(x < 0){ int b;
sign = -1;
x = -x; int absx;
int absy;
int absz;
int o;
uchar *e;
uchar p[128];
};
static void
ptrparse(int t, int f, int g[], int l[], int, void *a)
{
int v, m;
Ptr *p = a;
if(t != Input)
return;
if(g[RepId] != 0){
if(p->p[0] != g[RepId]){
p->o = 0;
return;
} }
switch(x){ if(p->o < 8)
case 0: p->o = 8; /* skip report id byte */
case 1: }
case 2: v = getbits(p->p, p->e, g[RepSize], p->o);
case 3: 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; break;
case 4: case 0x010030:
x = 6 + (accel>>2); if((f & (Fabs|Frel)) == Fabs){
p->x = (v - p->absx);
p->absx = v;
} else {
p->x = v;
p->absx += v;
}
break; break;
case 5: case 0x010031:
x = 9 + (accel>>1); if((f & (Fabs|Frel)) == Fabs){
p->y = (v - p->absy);
p->absy = v;
} else {
p->y = v;
p->absy += v;
}
break; break;
default: case 0x010038:
x *= MaxAcc; 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; break;
} }
return sign*x; }
} p->o += g[RepSize];
static short
s16(void *p)
{
uchar *b = p;
return b[0] | b[1]<<8;
} }
static void static void
ptrwork(void* a) ptrwork(void* a)
{ {
static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7}; char err[ERRMAX];
int x, y, z, b, c, nerrs, skiplead;
char err[ERRMAX], buf[64];
char mbuf[80]; char mbuf[80];
int c, nerrs;
KDev* f = a; KDev* f = a;
Ptr p;
kbprocname(f, "ptrwork"); kbprocname(f, "ptr");
sethipri(); sethipri();
skiplead = -1; memset(&p, 0, sizeof(p));
nerrs = 0; nerrs = 0;
for(;;){ for(;;){
if(f->ep == nil) if(f->ep == nil)
kbfatal(f, nil); kbfatal(f, nil);
if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf) if(f->ep->maxpkt < 1 || f->ep->maxpkt > sizeof(p.p))
kbfatal(f, "mouse: weird mouse maxpkt"); kbfatal(f, "ptr: weird mouse maxpkt");
memset(buf, 0, sizeof buf);
c = read(f->ep->dfd, buf, f->ep->maxpkt); memset(p.p, 0, sizeof(p.p));
c = read(f->ep->dfd, p.p, f->ep->maxpkt);
if(c <= 0){ if(c <= 0){
if(c < 0) if(c < 0)
rerrstr(err, sizeof(err)); rerrstr(err, sizeof(err));
else else
strcpy(err, "zero read"); strcpy(err, "zero read");
if(++nerrs < 3){ 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) if(strstr(err, "babble") != 0)
recoverkb(f); recoverkb(f);
continue; continue;
@ -249,47 +501,12 @@ ptrwork(void* a)
kbfatal(f, err); kbfatal(f, err);
} }
nerrs = 0; nerrs = 0;
if(c < 3)
continue;
if(c > 4){ p.o = 0;
/* p.e = p.p + c;
* horrible horrible hack. some mouse send a repparse(f->rep, f->rep+f->nrep, ptrparse, &p);
* 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;
}
z = 0; seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", p.x, p.y, p.b);
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);
if(write(f->infd, mbuf, strlen(mbuf)) < 0) if(write(f->infd, mbuf, strlen(mbuf)) < 0)
kbfatal(f, "mousein i/o"); kbfatal(f, "mousein i/o");
} }
@ -479,7 +696,7 @@ kbdwork(void *a)
char err[128]; char err[128];
KDev *f = a; KDev *f = a;
kbprocname(f, "kbdwork"); kbprocname(f, "kbd");
f->repeatc = chancreate(sizeof(ulong), 0); f->repeatc = chancreate(sizeof(ulong), 0);
if(f->repeatc == nil) if(f->repeatc == nil)
@ -516,7 +733,7 @@ kbdwork(void *a)
continue; continue;
if(kbdbusy(buf + 2, c - 2)) if(kbdbusy(buf + 2, c - 2))
continue; continue;
if(usbdebug > 2 || kbdebug > 1){ if(usbdebug > 2 || debug > 1){
fprint(2, "kbd mod %x: ", buf[0]); fprint(2, "kbd mod %x: ", buf[0]);
for(i = 2; i < c; i++) for(i = 2; i < c; i++)
fprint(2, "kc %x ", buf[i]); fprint(2, "kc %x ", buf[i]);
@ -540,8 +757,8 @@ kbstart(Dev *d, Ep *ep, char *infile, void (*f)(void*))
} }
incref(d); incref(d);
kd->dev = d; kd->dev = d;
if(setbootproto(kd, ep->id) < 0){ if(setproto(kd, ep->id) < 0){
fprint(2, "%s: %s: bootproto: %r\n", argv0, d->dir); fprint(2, "%s: %s: setproto: %r\n", argv0, d->dir);
goto Err; goto Err;
} }
kd->ep = openep(kd->dev, ep->id); kd->ep = openep(kd->dev, ep->id);
@ -576,10 +793,9 @@ threadmain(int argc, char* argv[])
ARGBEGIN{ ARGBEGIN{
case 'a': case 'a':
accel = strtol(EARGF(usage()), nil, 0);
break; break;
case 'd': case 'd':
kbdebug++; debug++;
break; break;
default: default:
usage(); usage();