diff options
author | aiju <devnull@localhost> | 2014-03-09 18:03:07 +0100 |
---|---|---|
committer | aiju <devnull@localhost> | 2014-03-09 18:03:07 +0100 |
commit | 26a8accad26267678c240e48a8544a208d9ba571 (patch) | |
tree | cf358a416a46e7aa0b62a6b35affa3ac6afc0385 /sys | |
parent | 62ffb9c16e0e0490404f3b0a7c2c7c560fd56ec4 (diff) |
nusb: added joy
Diffstat (limited to 'sys')
-rw-r--r-- | sys/src/cmd/nusb/joy/hid.h | 77 | ||||
-rw-r--r-- | sys/src/cmd/nusb/joy/joy.c | 436 | ||||
-rw-r--r-- | sys/src/cmd/nusb/joy/mkfile | 20 | ||||
-rw-r--r-- | sys/src/cmd/nusb/mkfile | 1 |
4 files changed, 534 insertions, 0 deletions
diff --git a/sys/src/cmd/nusb/joy/hid.h b/sys/src/cmd/nusb/joy/hid.h new file mode 100644 index 000000000..7f849ad9d --- /dev/null +++ b/sys/src/cmd/nusb/joy/hid.h @@ -0,0 +1,77 @@ +/* + * USB joystick constants + */ +enum { + + Stack = 32 * 1024, + + /* HID class subclass protocol ids */ + JoyCSP = 0x000003, + + Maxaxes = 3, + + /* Requests */ + Getreport = 0x01, + Setreport = 0x09, + Getproto = 0x03, + Setproto = 0x0b, + + /* protocols for SET_PROTO request */ + Bootproto = 0, + Reportproto = 1, + + /* protocols for SET_REPORT request */ + 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, +}; diff --git a/sys/src/cmd/nusb/joy/joy.c b/sys/src/cmd/nusb/joy/joy.c new file mode 100644 index 000000000..d63199b62 --- /dev/null +++ b/sys/src/cmd/nusb/joy/joy.c @@ -0,0 +1,436 @@ +/* + * 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.bv + * + */ + +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "usb.h" +#include "hid.h" + +typedef struct KDev KDev; +struct KDev +{ + Dev* dev; /* usb device*/ + Dev* ep; /* endpoint to get events */ + + /* report descriptor */ + int nrep; + uchar rep[512]; +}; + +static void +kbfree(KDev *kd) +{ + if(kd->ep != nil) + closedev(kd->ep); + if(kd->dev != nil) + closedev(kd->dev); + free(kd); +} + +static void +kbfatal(KDev *kd, char *sts) +{ + if(sts != nil) + fprint(2, "%s: fatal: %s\n", argv0, sts); + else + fprint(2, "%s: exiting\n", argv0); + kbfree(kd); + threadexits(sts); +} + +static int debug, kbd; + +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 +setproto(KDev *f, int eid) +{ + int id, proto; + + proto = Bootproto; + id = f->dev->usb->ep[eid]->iface->id; + 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 + kbfatal(f, "no report"); + return usbcmd(f->dev, Rh2d|Rclass|Riface, Setproto, proto, id, nil, 0); +} + +static void +kbprocname(KDev *kd, char *name) +{ + char buf[128]; + snprint(buf, sizeof(buf), "%s %s", name, kd->ep->dir); + threadsetname(buf); +} + +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); +} + +typedef struct Joy Joy; +struct Joy +{ + int axes[Maxaxes]; + int oldaxes[Maxaxes]; + u64int btns; + + int o; + uchar *e; + uchar p[128]; +}; + +static void +joyparse(int t, int f, int g[], int l[], int, void *a) +{ + int v, i; + Joy *p = a; + u64int m; + + 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]){ + switch(l[Usage]){ + case 0x010030: + case 0x010031: + case 0x010032: + i = l[Usage] - 0x010030; + if((f & (Fabs|Frel)) == Fabs) + p->axes[i] = v; + else + p->axes[i] += v; + break; + } + if((l[Usage] >> 16) == 0x09){ + m = 1ULL << (l[Usage] & 0xff); + p->btns &= ~m; + if(v != 0) + p->btns |= m; + } + } + p->o += g[RepSize]; +} + +static int +kbdbusy(uchar* buf, int n) +{ + int i; + + for(i = 1; i < n; i++) + if(buf[i] == 0 || buf[i] != buf[0]) + return 0; + return 1; +} + +static void +joywork(void *a) +{ + char err[ERRMAX]; + int i, c, nerrs; + KDev* f = a; + Joy p; + u64int lastb; + + kbprocname(f, "joy"); + sethipri(); + + memset(&p, 0, sizeof(p)); + lastb = 0; + + nerrs = 0; + for(;;){ + if(f->ep == nil) + kbfatal(f, nil); + if(f->ep->maxpkt < 1 || f->ep->maxpkt > sizeof(p.p)) + kbfatal(f, "joy: 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: joy: %s: read: %s\n", argv0, f->ep->dir, err); + continue; + } + kbfatal(f, err); + } + nerrs = 0; + + p.o = 0; + p.e = p.p + c; + repparse(f->rep, f->rep+f->nrep, joyparse, &p); + for(i = 0; i < Maxaxes; i++){ + if(p.axes[i] != p.oldaxes[i]) + print("axis %d %d\n", i, p.axes[i]); + p.oldaxes[i] = p.axes[i]; + } + for(i = 0; i < 64; i++) + if(((lastb ^ p.btns) & (1ULL<<i)) != 0) + print("%s %d\n", (p.btns & (1ULL<<i)) != 0 ? "down" : "up", i); + lastb = p.btns; + } +} + +static void +kbstart(Dev *d, Ep *ep, void (*f)(void*)) +{ + KDev *kd; + + kd = emallocz(sizeof(KDev), 1); + incref(d); + kd->dev = d; + 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); + if(kd->ep == nil){ + fprint(2, "%s: %s: openep %d: %r\n", argv0, d->dir, ep->id); + goto Err; + } + if(opendevdata(kd->ep, OREAD) < 0){ + fprint(2, "%s: %s: opendevdata: %r\n", argv0, kd->ep->dir); + goto Err; + } + f(kd); + return; +Err: + kbfree(kd); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-d] devid\n", argv0); + threadexits("usage"); +} + +void +threadmain(int argc, char* argv[]) +{ + int i; + Dev *d; + Ep *ep; + Usbdev *ud; + + ARGBEGIN{ + case 'd': + debug++; + break; + default: + usage(); + }ARGEND; + if(argc != 1) + usage(); + d = getdev(atoi(*argv)); + if(d == nil) + sysfatal("getdev: %r"); + ud = d->usb; + ep = nil; + for(i = 0; i < nelem(ud->ep); i++){ + if((ep = ud->ep[i]) == nil) + continue; + if(ep->type == Eintr && ep->dir == Ein && ep->iface->csp == JoyCSP) + break; + } + if(ep == nil) + sysfatal("no suitable endpoint found"); + kbstart(d, ep, joywork); + threadexits(nil); +} diff --git a/sys/src/cmd/nusb/joy/mkfile b/sys/src/cmd/nusb/joy/mkfile new file mode 100644 index 000000000..63af40506 --- /dev/null +++ b/sys/src/cmd/nusb/joy/mkfile @@ -0,0 +1,20 @@ +</$objtype/mkfile + +TARG=joy +OFILES=joy.$O +HFILES=\ + ../lib/usb.h\ + hid.h\ + +LIB=../lib/usb.a$O + +BIN=/$objtype/bin/nusb + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +</sys/src/cmd/mkone +CFLAGS=-I../lib $CFLAGS + diff --git a/sys/src/cmd/nusb/mkfile b/sys/src/cmd/nusb/mkfile index c63a4be52..4dd113a50 100644 --- a/sys/src/cmd/nusb/mkfile +++ b/sys/src/cmd/nusb/mkfile @@ -9,6 +9,7 @@ DIRS=\ disk\ serial\ ptp\ + joy\ UPDATE=\ mkfile\ |