Some of the mice we have have a broken boot protocol. For some of them the wheel does not work, for others, they stop sending anything after enough inactivity. Dell mice are specially bad this way. This patch tries to set up the mice properly using the hid report descriptor and so on. Falls back to boot protocol if something does not work. Just in case for now, added a -b to force boot protocol. I have to find a mouse that does not work properly with this patch, though. -b is left undocumented for now, should probably disappear once enough mice have been tried. Notes: Wed Nov 16 14:31:37 EST 2011 geoff code tidied up before acceptance; please replace yours with the version now on sources. thanks. Reference: /n/sources/patch/applied/mice Date: Sun Nov 13 18:35:57 CET 2011 Signed-off-by: paurea@lsub.org Reviewed-by: geoff --- /sys/src/cmd/usb/kb/hid.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/usb/kb/hid.c Sun Nov 13 18:35:17 2011 @@ -0,0 +1,208 @@ +#include +#include +#include +#include "usb.h" +#include "hid.h" + +/* + * Rough hid descriptor parsing and interpretation for mice + * + * Chain and its operations build the infrastructure needed + * to manipulate non-aligned fields which do appear (sigh!). + */ + +/* Get, at most, 8 bits*/ +static uchar +get8bits(Chain *ch, int nbits) +{ + int b, nbyb, nbib, nlb; + uchar low, high, res; + + b = ch->b+nbits-1; + nbib = ch->b % 8; + nbyb = ch->b / 8; + nlb = 8 - nbib; + if(nlb > nbits) + nlb = nbits; + + low = MSK(nlb) & (ch->buf[nbyb] >> nbib); + if(IsCut(ch->b, b)) + high = (ch->buf[nbyb + 1] & MSK(nbib)) << nlb; + else + high = 0; + res = high | low; + ch->b += nbits; + return res; +} + +static void +getbits(void *p, Chain *ch, int nbits) +{ + int nby, nbi, i; + uchar *vp; + + assert(ch->e >= ch->b); + nby = nbits / 8; + nbi = nbits % 8; + + vp = p; + for(i = 0; i < nby; i++) + *vp++ = get8bits(ch, 8); + + if(nbi != 0) + *vp = get8bits(ch, nbi); +} + +/* TODO check report id, when it does appear (not all devices) */ +int +parsereportdesc(HidRepTempl *temp, uchar *repdesc, int repsz) +{ + int i, j, l, n, isptr, hasxy, hasbut, nk; + int ks[MaxVals]; + HidInterface *ifs; + + ifs = temp->ifcs; + isptr = 0; + hasxy = hasbut = 0; + n = 0; + nk = 0; + memset(ifs, 0, sizeof(*ifs)*MaxIfc); + for(i = 0; i < repsz/2; i+=2){ + if(n == MaxIfc) + break; + if(repdesc[i] == HidEnd) + break; + + switch(repdesc[i]){ + default: + break; + case HidTypeUsg: + switch(repdesc[i+1]){ + default: + break; + case HidX: + hasxy++; + ks[nk++] = KindX; + break; + case HidY: + hasxy++; + ks[nk++] = KindY; + break; + case HidWheel: + ks[nk++] = KindWheel; + break; + case HidPtr: + isptr++; + break; + + } + break; + case HidTypeUsgPg: + switch(repdesc[i+1]){ + default: + break; + case HidPgButts: + hasbut++; + ks[nk++] = KindButtons; + break; + } + break; + case HidTypeRepSz: + ifs[n].nbits = repdesc[i+1]; + break; + case HidTypeCnt: + ifs[n].count = repdesc[i+1]; + break; + case HidInput: + for(j = 0; j nifcs = n; + if(isptr && hasxy && hasbut) + return 0; + else + fprint(2, "bad report: isptr %d, hasxy %d, hasbut %d\n", + isptr, hasxy, hasbut); + + return -1; +} + +int +parsereport(HidRepTempl *templ, Chain *rep) +{ + int i, j, k, ifssz; + ulong u; + uchar *p; + HidInterface *ifs; + + ifssz = templ->nifcs; + ifs = templ->ifcs; + for(i = 0; i < ifssz; i++){ + for(j = 0; j < ifs[i].count; j++){ + if(ifs[i].nbits > 8*sizeof(ifs[i].v[0])){ + fprint(2, "ptr: bad bits in parsereport"); + return -1; + } + u =0; + getbits(&u, rep, ifs[i].nbits); + p = (uchar *)&u; + ifs[i].v[j] = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|(p[0]<<0); /* le to host */ + k = ifs[i].kind[j]; + if(k == KindX || k == KindY || k == KindWheel){ + if(ifs[i].v[j] & 1<nifcs; + ifs = templ->ifcs; + for(i = 0; i < ifssz; i++){ + fprint(2, "\tcount %#ux", ifs[i].count); + fprint(2, " nbits %d ", ifs[i].nbits); + fprint(2, "\n"); + for(j = 0; j < ifs[i].count; j++){ + fprint(2, "\t\tkind %#ux ", ifs[i].kind[j]); + fprint(2, "v %#lux\n", ifs[i].v[j]); + } + fprint(2, "\n"); + } + fprint(2, "\n"); +} + + +/* could cache indexes after parsing the descriptor */ +int +hidifcval(HidRepTempl *templ, int kind, int n) +{ + int i, j, ifssz; + HidInterface *ifs; + + ifssz = templ->nifcs; + ifs = templ->ifcs; + assert(n <= nelem(ifs[i].v)); + for(i = 0; i < ifssz; i++){ + for(j = 0; j < ifs[i].count; j++) + if(ifs[i].kind[j] == kind && n-- == 0){ + return (int)ifs[i].v[j]; + } + } + return 0; /* least damage (no buttons, no movement) */ +} --- /sys/src/cmd/usb/kb/hid.h Sun Nov 13 18:35:21 2011 +++ /sys/src/cmd/usb/kb/hid.h Sun Nov 13 18:35:19 2011 @@ -1,6 +1,11 @@ /* * USB keyboard/mouse constants */ + +typedef struct Chain Chain; +typedef struct HidInterface HidInterface; +typedef struct HidRepTempl HidRepTempl; + enum { Stack = 32 * 1024, @@ -11,10 +16,11 @@ /* Requests */ Getproto = 0x03, + Setidle = 0x0a, Setproto = 0x0b, /* protocols for SET_PROTO request */ - Bootproto = 0, + Bootproto = 0, Reportproto = 1, }; @@ -22,7 +28,7 @@ /* keyboard modifier bits */ Mlctrl = 0, Mlshift = 1, - Mlalt = 2, + Mlalt = 2, Mlgui = 3, Mrctrl = 4, Mrshift = 5, @@ -32,10 +38,10 @@ /* masks for byte[0] */ Mctrl = 1< 0) + +enum { + KindPad = 0, + KindButtons, + KindX, + KindY, + KindWheel, + + MaxVals = 8, + MaxIfc = 8, +}; + +struct HidInterface { + ulong v[MaxVals]; /* one ulong per val should be enough */ + int kind[MaxVals]; + int nbits; + int count; +}; + +struct HidRepTempl{ + int nifcs; + HidInterface ifcs[MaxIfc]; +}; + +enum { + /* report types */ + HidTypeUsgPg = 0x05, + HidPgButts = 0x09, + + HidTypeRepSz = 0x75, + HidTypeCnt = 0x95, + + HidTypeUsg = 0x09, + HidPtr = 0x01, + HidX = 0x30, + HidY = 0x31, + HidWheel = 0x38, + + HidInput = 0x81, + HidReportId = 0x85, + + HidEnd = 0x0c, +}; + +int parsereportdesc(HidRepTempl *temp, uchar *repdesc, int repsz); +void dumpreport(HidRepTempl *templ); +int parsereport(HidRepTempl *templ, Chain *rep); +int hidifcval(HidRepTempl *templ, int kind, int n); --- /sys/src/cmd/usb/kb/kb.c Sun Nov 13 18:35:26 2011 +++ /sys/src/cmd/usb/kb/kb.c Sun Nov 13 18:35:23 2011 @@ -7,7 +7,9 @@ * Mouse events are converted to the format of mouse(3)'s * mousein file. * Keyboard keycodes are translated to scan codes and sent to kbin(3). - * + * + * If there is no keyboard, it tries to setup the mouse properly, else it falls + * back to boot protocol. */ #include @@ -32,6 +34,9 @@ Kin* in; /* used to send events to kernel */ Channel*repeatc; /* only for keyboard */ int accel; /* only for mouse */ + int bootp; /* has associated keyboard */ + HidRepTempl templ; + int (*ptrvals)(KDev *kd, Chain *ch, int *px, int *py, int *pb); }; /* @@ -114,16 +119,60 @@ static int kbdebug; +static int ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb); +static int ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb); + static int -setbootproto(KDev* f, int eid) +setbootproto(KDev* f, int eid, uchar *, int) { int r, id; + f->ptrvals = ptrbootpvals; r = Rh2d|Rclass|Riface; + dprint(2, "setting boot protocol\n"); id = f->dev->usb->ep[eid]->iface->id; return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0); } +static uchar ignoredesc[128]; +static int +setfirstconfig(KDev* f, int eid, uchar *desc, int descsz) +{ + int nr, r, id, i; + + dprint(2, "setting first config\n"); + if(desc == nil){ + descsz = sizeof ignoredesc; + desc = ignoredesc; + } + id = f->dev->usb->ep[eid]->iface->id; + r = Rh2d | Rstd | Rdev; + nr =usbcmd(f->dev, r, Rsetconf, 1, id, nil, 0); + if(nr < 0) + return -1; + r = Rh2d | Rclass | Riface; + nr=usbcmd(f->dev, r, Setidle, 0, id, nil, 0); + if(nr < 0) + return -1; + r = Rd2h | Rstd | Riface; + nr=usbcmd(f->dev, r, Rgetdesc, Dreport<<8, id, desc, descsz); + if(nr < 0) + return -1; + if(kbdebug && nr > 0){ + fprint(2, "report descriptor: "); + for(i = 0; i < nr; i++){ + fprint(2, " %#2.2ux ", desc[i]); + if(i!= 0 && i%8 == 0) + fprint(2, "\n"); + } + fprint(2, "\n"); + } + f->ptrvals = ptrrepvals; + return nr; + +} + + /* * Try to recover from a babble error. A port reset is the only way out. * BUG: we should be careful not to reset a bundle with several devices. @@ -136,9 +185,14 @@ close(f->dev->dfd); /* it's for usbd now */ devctl(f->dev, "reset"); for(i = 0; i < 10; i++){ + if(i == 5) + f->bootp++; sleep(500); if(opendevdata(f->dev, ORDWR) >= 0){ - setbootproto(f, f->ep->id); + if(f->bootp) + setbootproto(f, f->ep->id, nil, 0); /* TODO func pointer */ + else + setfirstconfig(f, f->ep->id, nil, 0); break; } /* else usbd still working... */ @@ -215,13 +269,68 @@ close(fd); } +static int +ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb) +{ + int x, y, b, c; + static char buts[] = {0x0, 0x2, 0x1}; + int i; + + c = ch->e/8; + parsereport(&kd->templ, ch); + + if(kbdebug) + dumpreport(&kd->templ); + if(c < 3) + return -1; + x = hidifcval(&kd->templ, KindX, 0); + y = hidifcval(&kd->templ, KindY, 0); + b = 0; + for(i = 0; itempl, KindButtons, i)&1)< 3 && hidifcval(&kd->templ, KindWheel, 0) > 0) /* up */ + b |= 0x10; + if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) < 0) /* down */ + b |= 0x08; + + *px = x; + *py = y; + *pb = b; + return 0; +} + +static int +ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb) +{ + char x, y; + int b, c; + static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7}; + + c = ch->e/8; + + if(c < 3) + return -1; + x = hidifcval(&kd->templ, KindX, 0); + y = hidifcval(&kd->templ, KindY, 0); + + b = maptab[ch->buf[0] & 0x7]; + if(c > 3 && ch->buf[3] == 1) /* up */ + b |= 0x08; + if(c > 3 && ch->buf[3] == 0xff) /* down */ + b |= 0x10; + *px = x; + *py = y; + *pb = b; + return 0; +} + + static void ptrwork(void* a) { - static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7}; + Chain ch; int x, y, b, c, ptrfd; int mfd, nerrs; - char buf[32]; char mbuf[80]; KDev* f = a; int hipri; @@ -229,14 +338,13 @@ hipri = nerrs = 0; ptrfd = f->ep->dfd; mfd = f->in->fd; - - if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf) + if(f->ep->maxpkt < 3 || f->ep->maxpkt > MaxChLen) kbfatal(f, "weird mouse maxpkt"); for(;;){ - memset(buf, 0, sizeof buf); + memset(ch.buf, 0, MaxChLen); if(f->ep == nil) kbfatal(f, nil); - c = read(ptrfd, buf, f->ep->maxpkt); + c = read(ptrfd, ch.buf, f->ep->maxpkt); assert(f->dev != nil); assert(f->ep != nil); if(c < 0){ @@ -248,20 +356,15 @@ } if(c <= 0) kbfatal(f, nil); - if(c < 3) - continue; + ch.b = 0; + ch.e = 8*c; + if(f->ptrvals(f, &ch, &x, &y, &b) < 0) + continue; + if(f->accel){ - x = scale(f, buf[1]); - y = scale(f, buf[2]); - }else{ - x = buf[1]; - y = buf[2]; - } - b = maptab[buf[0] & 0x7]; - if(c > 3 && buf[3] == 1) /* up */ - b |= 0x08; - if(c > 3 && buf[3] == -1) /* down */ - b |= 0x10; + x = scale(f, x); + y = scale(f, y); + } if(kbdebug > 1) fprint(2, "kb: m%11d %11d %11d\n", x, y, b); seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b); @@ -502,9 +605,10 @@ } static void -kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), int accel) +kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd) { - KDev *kd; + uchar desc[128]; + int res; qlock(&inlck); if(in->fd < 0){ @@ -517,15 +621,25 @@ } in->ref++; /* for kd->in = in */ qunlock(&inlck); - kd = d->aux = emallocz(sizeof(KDev), 1); d->free = freekdev; kd->in = in; kd->dev = d; - if(setbootproto(kd, ep->id) < 0){ - fprint(2, "kb: %s: bootproto: %r\n", d->dir); - return; - } - kd->accel = accel; + res = -1; + if(!kd->bootp){ + res= setfirstconfig(kd, ep->id, desc, sizeof desc); + } + if(res > 0){ + res = parsereportdesc(&kd->templ, desc, sizeof desc); + } + /* if we could not set the first config, we give up */ + if(kd->bootp || res < 0){ + kd->bootp = 1; + if(setbootproto(kd, ep->id, nil, 0) < 0){ + fprint(2, "kb: %s: bootproto: %r\n", d->dir); + return; + } + }else if(kbdebug) + dumpreport(&kd->templ); kd->ep = openep(d, ep->id); if(kd->ep == nil){ fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id); @@ -545,7 +659,7 @@ static int usage(void) { - werrstr("usage: usb/kb [-dkm] [-a n] [-N nb]"); + werrstr("usage: usb/kb [-dkmb] [-a n] [-N nb]"); return -1; } @@ -555,8 +669,11 @@ int i, kena, pena, accel, devid; Usbdev *ud; Ep *ep; + int bootp; + KDev *kd; kena = pena = 1; + bootp = 0; accel = 0; devid = d->id; ARGBEGIN{ @@ -577,6 +694,9 @@ case 'N': devid = atoi(EARGF(usage())); /* ignore dev number */ break; + case 'b': + bootp++; + break; default: return usage(); }ARGEND; @@ -587,15 +707,33 @@ ud = d->usb; d->aux = nil; dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref); + + if(kena) + for(i = 0; i < nelem(ud->ep); i++){ + if((ep = ud->ep[i]) == nil) + break; + if(ep->iface->csp == KbdCSP) + bootp = 1; + } + for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[i]) == nil) break; if(kena && ep->type == Eintr && ep->dir == Ein) - if(ep->iface->csp == KbdCSP) - kbstart(d, ep, &kbdin, kbdwork, accel); + if(ep->iface->csp == KbdCSP){ + kd = d->aux = emallocz(sizeof(KDev), 1); + kd->accel = 0; + kd->bootp = 1; + kbstart(d, ep, &kbdin, kbdwork, kd); + } if(pena && ep->type == Eintr && ep->dir == Ein) - if(ep->iface->csp == PtrCSP) - kbstart(d, ep, &ptrin, ptrwork, accel); + if(ep->iface->csp == PtrCSP){ + + kd = d->aux = emallocz(sizeof(KDev), 1); + kd->accel = accel; + kd->bootp = bootp; + kbstart(d, ep, &ptrin, ptrwork, kd); + } } return 0; } --- /sys/src/cmd/usb/kb/main.c Sun Nov 13 18:35:31 2011 +++ /sys/src/cmd/usb/kb/main.c Sun Nov 13 18:35:28 2011 @@ -16,7 +16,7 @@ static void usage(void) { - fprint(2, "usage: %s [-dkm] [-a n] [-N nb] [dev...]\n", argv0); + fprint(2, "usage: %s [-dkmb] [-a n] [-N nb] [dev...]\n", argv0); threadexitsall("usage"); } @@ -52,6 +52,9 @@ case 'N': devid = atoi(EARGF(usage())); /* ignore dev number */ USED(devid); + break; + case 'b': + as = seprint(as, ae, " -b"); break; default: usage(); --- /sys/src/cmd/usb/kb/mkfile Sun Nov 13 18:35:35 2011 +++ /sys/src/cmd/usb/kb/mkfile Sun Nov 13 18:35:33 2011 @@ -2,7 +2,9 @@ TARG=kb OFILES=main.$O -LIBDOFILES=kb.$O +LIBDOFILES=kb.$O\ + hid.$O + HFILES=\ ../lib/usb.h\ hid.h\