let the kernel track led changes. this means that keyboard mappings are applied before led state changes are determined. this means that if you choose to turn caps lock on, it will work with usb keyboards. (assuming an appropriate kernel) also, don't try to keep just one fd open to kbin. keep one open per keyboard. this means that control/shift state won't bleed from one keyboard to the next. Reference: /n/atom/patch/applied/usbkbledfix Date: Fri May 2 22:46:43 CES 2014 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/usb/kb/kb.c Fri May 2 22:44:58 2014 +++ /sys/src/cmd/usb/kb/kb.c Fri May 2 22:44:58 2014 @@ -32,18 +32,20 @@ typedef struct KDev KDev; typedef struct Kbd Kbd; typedef struct Mouse Mouse; -typedef struct Kin Kin; struct Kbd { Channel* repeatc; Channel* exitc; - long nproc; + Channel* msgc; + int nproc; uint led; + uint readmsg; }; struct Mouse { + int (*ptrvals)(KDev *kd, Chain *ch, int *px, int *py, int *pb); int accel; /* only for mouse */ }; @@ -52,24 +54,15 @@ Dev* dev; /* usb device*/ Dev* ep; /* endpoint to get events */ int eid; /* id of endpoint (not of open ep) */ - Kin* in; /* used to send events to kernel */ + int fd; /* used to send events to kernel */ int idle; /* min time between reports (× 4ms) */ int bootp; /* has associated keyboard */ int debug; + int exiting; /* bogus */ Kbd; Mouse; HidRepTempl templ; - int (*ptrvals)(KDev *kd, Chain *ch, int *px, int *py, int *pb); -}; - -/* - * Kbdin and mousein files must be shared among all instances. - */ -struct Kin -{ - int ref; - int fd; - char* name; + void (*work)(void*); }; /* @@ -166,20 +159,6 @@ }; static Scan *sctab = sctaben; -static QLock inlck; -static Kin kbdin = -{ - .ref = 0, - .name = "#Ι/kbin", - .fd = -1, -}; -static Kin ptrin = -{ - .ref = 0, - .name = "#m/mousein", - .fd = -1, -}; - 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); @@ -319,6 +298,7 @@ } } +static void putscan(KDev *f, Scan sc); static void kbfatal(KDev *kd, char *sts) { @@ -328,15 +308,27 @@ fprint(2, "kb: fatal: %s\n", sts); else fprint(2, "kb: exiting\n"); - if(kd->repeatc != nil){ + kd->exiting = 1; + putscan(kd, 0x0045); /* ledproc gets stuck: debug this */ + + close(kd->fd); + kd->fd = -1; + + if(kd->repeatc != nil) chanclose(kd->repeatc); - for(; kd->nproc != 0; kd->nproc--) - recvul(kd->exitc); + if(kd->msgc != nil) + chanclose(kd->msgc); + + for(; kd->nproc != 0; kd->nproc--) + recvul(kd->exitc); + if(kd->repeatc != nil) chanfree(kd->repeatc); - chanfree(kd->exitc); - kd->repeatc = nil; - kd->exitc = nil; - } + if(kd->msgc != nil) + chanfree(kd->msgc); + chanfree(kd->exitc); + kd->repeatc = nil; + kd->exitc = nil; + kd->msgc = nil; dev = kd->dev; kd->dev = nil; if(kd->ep != nil) @@ -473,7 +465,7 @@ threadsetname("ptr %s", f->ep->dir); hipri = nerrs = 0; ptrfd = f->ep->dfd; - mfd = f->in->fd; + mfd = f->fd; if(f->ep->maxpkt < 3 || f->ep->maxpkt > MaxChLen) kbfatal(f, "weird mouse maxpkt"); for(;;){ @@ -536,9 +528,9 @@ s[0] = sc>>8; s[1] = sc; if(sc >= 0x100) - write(f->in->fd, s, 2); + write(f->fd, s, 2); else - write(f->in->fd, s+1, 1); + write(f->fd, s+1, 1); } static void @@ -599,6 +591,27 @@ } static void +msgproc(void* a) +{ + char buf[64+1]; + int n; + KDev *f; + + threadsetname("kbd led"); + f = a; + for(;;){ + n = read(f->fd, buf, sizeof buf-1); + if(n <= 0 || f->exiting){ +fprint(2, "kb: %d: msgproc exiting... n=%d err=%r\n", getpid(), n); + sendul(f->exitc, Exiting); + threadexits("fd closed"); + } + buf[n] = 0; + sendp(f->msgc, strdup(buf)); + } +} + +static void putmod(KDev *f, uchar mods, uchar omods, uchar mask, Scan sc) { /* BUG: Should be a single write */ @@ -656,6 +669,10 @@ case 0x41: f->debug = 0; break; + } + + if(!f->readmsg) /* backwards compat */ + switch(buf[i]){ case 0x53: f->led ^= Lnum; setled(f, f->led); @@ -708,11 +725,37 @@ return 1; } +int +ledcvt(char *s) +{ + int c, led; + + led = 0; + while(c = *s++){ + switch(c){ + case 's': + led |= Lscroll; + break; + case 'c': + led |= Lcaps; + break; + case 'n': + led |= Lnum; + break; + case 'k': + led |= Lkana; + break; + } + } + return led; +} + static void kbdwork(void *a) { - int c, kbdfd, nerrs; + char *s; uchar buf[64], lbuf[64]; + int c, kbdfd, nerrs; char err[ERRMAX]; Scan dk; KDev *f = a; @@ -732,15 +775,36 @@ f->repeatc = chancreate(sizeof(ulong), 0); if(f->repeatc == nil){ chanfree(f->exitc); + f->exitc = nil; + kbfatal(f, "chancreate failed"); + } + f->msgc = chancreate(sizeof(char*), 2); + if(f->msgc == nil){ + chanfree(f->exitc); + f->exitc = nil; + chanfree(f->repeatc); + f->repeatc = nil; kbfatal(f, "chancreate failed"); } - f->nproc = 2; proccreate(repeatproc, f, Stack); proccreate(repeattimerproc, f, Stack); + if(f->readmsg){ + f->nproc++; + proccreate(msgproc, f, Stack); + } memset(lbuf, 0, sizeof lbuf); dk = nerrs = 0; for(;;){ + /* what if we just miss it? */ + while(f->msgc != nil && nbrecv(f->msgc, &s)){ + switch(*s){ + case 'L': + setled(f, f->led = ledcvt(s+1)); + break; + } + free(s); + } memset(buf, 0, sizeof buf); c = read(kbdfd, buf, f->ep->maxpkt); assert(f->dev != nil); @@ -774,45 +838,25 @@ KDev *kd; kd = a; - if(kd->in != nil){ - qlock(&inlck); - if(--kd->in->ref == 0){ - close(kd->in->fd); - kd->in->fd = -1; - } - qunlock(&inlck); - } dprint(2, "freekdev\n"); free(kd); } -static void -kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd) +static int +kbstart(Dev *d, Ep *ep, KDev *kd) { uchar *desc, otghack[128]; int n, res; - qlock(&inlck); - if(in->fd < 0){ - in->fd = open(in->name, OWRITE); - if(in->fd < 0){ - fprint(2, "kb: %s: %r\n", in->name); - qunlock(&inlck); - return; - } - } - in->ref++; /* for kd->in = in */ - qunlock(&inlck); d->free = freekdev; - kd->in = in; kd->dev = d; kd->ep = openep(d, ep->id); if(kd->ep == nil){ fprint(2, "kb: %s: workep: openep %d: %r\n", d->dir, ep->id); - return; + return -1; } kd->eid = ep->id; - if(in == &kbdin){ + if(kd->work == kbdwork){ /* * DWC OTG controller misses some split transaction inputs. * Set nonzero idle time to return more frequent reports @@ -837,7 +881,7 @@ kd->bootp = 1; if(setbootproto(kd, ep->id, nil) < 0){ fprint(2, "kb: %s: bootproto: %r\n", d->dir); - return; + return -1; } }else if(kd->debug) dumpreport(&kd->templ); @@ -845,11 +889,12 @@ fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir); closedev(kd->ep); kd->ep = nil; - return; + return -1; } incref(d); - proccreate(f, kd, Stack); + proccreate(kd->work, kd, Stack); + return 0; } static int @@ -916,23 +961,42 @@ for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[i]) == nil) continue; - if(kena && ep->type == Eintr && (ep->dir == Ein | ep->dir == Eboth) && - ep->iface->csp == KbdCSP){ + if(ep->type != Eintr || ep->dir != Ein && ep->dir != Eboth) + continue; + if(kena && ep->iface->csp == KbdCSP){ kd = d->aux = emallocz(sizeof(KDev), 1); kd->accel = 0; kd->bootp = 1; kd->debug = debug; - kbstart(d, ep, &kbdin, kbdwork, kd); + kd->work = kbdwork; + kd->fd = open("#Ι/kbin", ORDWR); + if(kd->fd >= 0) + kd->readmsg = 1; + else if(kd->fd == -1) + kd->fd = open("#Ι/kbin", OWRITE); + if(kd->fd == -1){ + fprint(2, "kb: #Ι/kbin: %r\n"); + free(kd); + }else if(kbstart(d, ep, kd) == -1){ + close(kd->fd); + free(kd); + } } - if(pena && ep->type == Eintr && (ep->dir == Ein | ep->dir == Eboth) && - ep->iface->csp == PtrCSP){ + if(pena && ep->iface->csp == PtrCSP){ kd = d->aux = emallocz(sizeof(KDev), 1); kd->accel = accel; kd->bootp = bootp; kd->debug = debug; - kbstart(d, ep, &ptrin, ptrwork, kd); + kd->work = ptrwork; + kd->fd = open("#m/mousein", OWRITE); + if(kd->fd == -1){ + // fprint(2, "kb: #m/mousein: %r\n"); + free(kd); + }else if(kbstart(d, ep, kd) == -1){ + close(kd->fd); + free(kd); + } } } return 0; } -