shithub: riscv

Download patch

ref: 26a8accad26267678c240e48a8544a208d9ba571
parent: 62ffb9c16e0e0490404f3b0a7c2c7c560fd56ec4
author: aiju <devnull@localhost>
date: Sun Mar 9 14:03:07 EDT 2014

nusb: added joy

diff: cannot open b/sys/src/cmd/nusb/joy//null: file does not exist: 'b/sys/src/cmd/nusb/joy//null'
--- /dev/null
+++ b/sys/src/cmd/nusb/joy/hid.h
@@ -1,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,
+};
--- /dev/null
+++ b/sys/src/cmd/nusb/joy/joy.c
@@ -1,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);
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/joy/mkfile
@@ -1,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
+
--- a/sys/src/cmd/nusb/mkfile
+++ b/sys/src/cmd/nusb/mkfile
@@ -9,6 +9,7 @@
 	disk\
 	serial\
 	ptp\
+	joy\
 
 UPDATE=\
 	mkfile\