xref: /plan9/sys/src/cmd/usb/kb/kb.c (revision 83030dd59048446fe2061c1188bbba3c96107b2b)
1*83030dd5SDavid du Colombier /*
2*83030dd5SDavid du Colombier  * USB Human Interaction Device: keyboard and mouse.
3*83030dd5SDavid du Colombier  *
4*83030dd5SDavid du Colombier  * If there's no usb keyboard, it tries to setup the mouse, if any.
5*83030dd5SDavid du Colombier  * It should be started at boot time.
6*83030dd5SDavid du Colombier  *
7*83030dd5SDavid du Colombier  * Mouse events are converted to the format of mouse(3)'s
8*83030dd5SDavid du Colombier  * mousein file.
9*83030dd5SDavid du Colombier  * Keyboard keycodes are translated to scan codes and sent to kbin(3).
10*83030dd5SDavid du Colombier  *
11*83030dd5SDavid du Colombier  */
12*83030dd5SDavid du Colombier 
13*83030dd5SDavid du Colombier #include <u.h>
14*83030dd5SDavid du Colombier #include <libc.h>
15*83030dd5SDavid du Colombier #include <thread.h>
16*83030dd5SDavid du Colombier #include "usb.h"
17*83030dd5SDavid du Colombier #include "hid.h"
18*83030dd5SDavid du Colombier 
19*83030dd5SDavid du Colombier /*
20*83030dd5SDavid du Colombier  * Map for the logitech bluetooth mouse with 8 buttons and wheels.
21*83030dd5SDavid du Colombier  *	{ ptr ->mouse}
22*83030dd5SDavid du Colombier  *	{ 0x01, 0x01 },	// left
23*83030dd5SDavid du Colombier  *	{ 0x04, 0x02 },	// middle
24*83030dd5SDavid du Colombier  *	{ 0x02, 0x04 },	// right
25*83030dd5SDavid du Colombier  *	{ 0x40, 0x08 },	// up
26*83030dd5SDavid du Colombier  *	{ 0x80, 0x10 },	// down
27*83030dd5SDavid du Colombier  *	{ 0x10, 0x08 },	// side up
28*83030dd5SDavid du Colombier  *	{ 0x08, 0x10 },	// side down
29*83030dd5SDavid du Colombier  *	{ 0x20, 0x02 }, // page
30*83030dd5SDavid du Colombier  * besides wheel and regular up/down report the 4th byte as 1/-1
31*83030dd5SDavid du Colombier  */
32*83030dd5SDavid du Colombier 
33*83030dd5SDavid du Colombier /*
34*83030dd5SDavid du Colombier  * key code to scan code; for the page table used by
35*83030dd5SDavid du Colombier  * the logitech bluetooth keyboard.
36*83030dd5SDavid du Colombier  */
37*83030dd5SDavid du Colombier char sctab[256] =
38*83030dd5SDavid du Colombier {
39*83030dd5SDavid du Colombier [0x00]	0x0,	0x0,	0x0,	0x0,	0x1e,	0x30,	0x2e,	0x20,
40*83030dd5SDavid du Colombier [0x08]	0x12,	0x21,	0x22,	0x23,	0x17,	0x24,	0x25,	0x26,
41*83030dd5SDavid du Colombier [0x10]	0x32,	0x31,	0x18,	0x19,	0x10,	0x13,	0x1f,	0x14,
42*83030dd5SDavid du Colombier [0x18]	0x16,	0x2f,	0x11,	0x2d,	0x15,	0x2c,	0x2,	0x3,
43*83030dd5SDavid du Colombier [0x20]	0x4,	0x5,	0x6,	0x7,	0x8,	0x9,	0xa,	0xb,
44*83030dd5SDavid du Colombier [0x28]	0x1c,	0x1,	0xe,	0xf,	0x39,	0xc,	0xd,	0x1a,
45*83030dd5SDavid du Colombier [0x30]	0x1b,	0x2b,	0x2b,	0x27,	0x28,	0x29,	0x33,	0x34,
46*83030dd5SDavid du Colombier [0x38]	0x35,	0x3a,	0x3b,	0x3c,	0x3d,	0x3e,	0x3f,	0x40,
47*83030dd5SDavid du Colombier [0x40]	0x41,	0x42,	0x43,	0x44,	0x57,	0x58,	0x63,	0x46,
48*83030dd5SDavid du Colombier [0x48]	0x77,	0x52,	0x47,	0x49,	0x53,	0x4f,	0x51,	0x4d,
49*83030dd5SDavid du Colombier [0x50]	0x4b,	0x50,	0x48,	0x45,	0x35,	0x37,	0x4a,	0x4e,
50*83030dd5SDavid du Colombier [0x58]	0x1c,	0x4f,	0x50,	0x51,	0x4b,	0x4c,	0x4d,	0x47,
51*83030dd5SDavid du Colombier [0x60]	0x48,	0x49,	0x52,	0x53,	0x56,	0x7f,	0x74,	0x75,
52*83030dd5SDavid du Colombier [0x68]	0x55,	0x59,	0x5a,	0x5b,	0x5c,	0x5d,	0x5e,	0x5f,
53*83030dd5SDavid du Colombier [0x70]	0x78,	0x79,	0x7a,	0x7b,	0x0,	0x0,	0x0,	0x0,
54*83030dd5SDavid du Colombier [0x78]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x71,
55*83030dd5SDavid du Colombier [0x80]	0x73,	0x72,	0x0,	0x0,	0x0,	0x7c,	0x0,	0x0,
56*83030dd5SDavid du Colombier [0x88]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
57*83030dd5SDavid du Colombier [0x90]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
58*83030dd5SDavid du Colombier [0x98]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
59*83030dd5SDavid du Colombier [0xa0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
60*83030dd5SDavid du Colombier [0xa8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
61*83030dd5SDavid du Colombier [0xb0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
62*83030dd5SDavid du Colombier [0xb8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
63*83030dd5SDavid du Colombier [0xc0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
64*83030dd5SDavid du Colombier [0xc8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
65*83030dd5SDavid du Colombier [0xd0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
66*83030dd5SDavid du Colombier [0xd8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
67*83030dd5SDavid du Colombier [0xe0]	0x1d,	0x2a,	0x38,	0x7d,	0x61,	0x36,	0x64,	0x7e,
68*83030dd5SDavid du Colombier [0xe8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x73,	0x72,	0x71,
69*83030dd5SDavid du Colombier [0xf0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
70*83030dd5SDavid du Colombier [0xf8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
71*83030dd5SDavid du Colombier };
72*83030dd5SDavid du Colombier 
73*83030dd5SDavid du Colombier typedef struct Dev Dev;
74*83030dd5SDavid du Colombier struct Dev {
75*83030dd5SDavid du Colombier 	char*	name;		/* debug */
76*83030dd5SDavid du Colombier 	void	(*proc)(void*);	/* handler process */
77*83030dd5SDavid du Colombier 	int	csp;		/* the csp we want */
78*83030dd5SDavid du Colombier 	int	enabled;	/* can we start it? */
79*83030dd5SDavid du Colombier 	int	ctlrno;		/* controller number, for probing*/
80*83030dd5SDavid du Colombier 	int	id;		/* device id, for probing*/
81*83030dd5SDavid du Colombier 	int	ep;		/* endpoint used to get events */
82*83030dd5SDavid du Colombier 	int	msz;		/* message size */
83*83030dd5SDavid du Colombier 	int	fd;		/* to the endpoint data file */
84*83030dd5SDavid du Colombier 	Device*	dev;		/* usb device*/
85*83030dd5SDavid du Colombier };
86*83030dd5SDavid du Colombier 
87*83030dd5SDavid du Colombier 
88*83030dd5SDavid du Colombier Dev	kf, pf;			/* kbd and pointer drivers*/
89*83030dd5SDavid du Colombier Channel *repeatc;		/* channel to request key repeating*/
90*83030dd5SDavid du Colombier 
91*83030dd5SDavid du Colombier void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
92*83030dd5SDavid du Colombier 	[STRING] pstring,
93*83030dd5SDavid du Colombier 	[DEVICE] pdevice,
94*83030dd5SDavid du Colombier 	[HID] phid,
95*83030dd5SDavid du Colombier };
96*83030dd5SDavid du Colombier 
97*83030dd5SDavid du Colombier int	accel;
98*83030dd5SDavid du Colombier int	hdebug;
99*83030dd5SDavid du Colombier int	dryrun;
100*83030dd5SDavid du Colombier int	verbose;
101*83030dd5SDavid du Colombier 
102*83030dd5SDavid du Colombier int mainstacksize = 32*1024;
103*83030dd5SDavid du Colombier 
104*83030dd5SDavid du Colombier int
105*83030dd5SDavid du Colombier robusthandler(void*, char *s)
106*83030dd5SDavid du Colombier {
107*83030dd5SDavid du Colombier 	if(hdebug)
108*83030dd5SDavid du Colombier 		fprint(2, "inthandler: %s\n", s);
109*83030dd5SDavid du Colombier 	return (s && (strstr(s, "interrupted")|| strstr(s, "hangup")));
110*83030dd5SDavid du Colombier }
111*83030dd5SDavid du Colombier 
112*83030dd5SDavid du Colombier long
113*83030dd5SDavid du Colombier robustread(int fd, void *buf, long sz)
114*83030dd5SDavid du Colombier {
115*83030dd5SDavid du Colombier 	long r;
116*83030dd5SDavid du Colombier 	char err[ERRMAX];
117*83030dd5SDavid du Colombier 
118*83030dd5SDavid du Colombier 	do {
119*83030dd5SDavid du Colombier 		r = read(fd , buf, sz);
120*83030dd5SDavid du Colombier 		if(r < 0)
121*83030dd5SDavid du Colombier 			rerrstr(err, sizeof err);
122*83030dd5SDavid du Colombier 	} while(r < 0 && robusthandler(nil, err));
123*83030dd5SDavid du Colombier 	return r;
124*83030dd5SDavid du Colombier }
125*83030dd5SDavid du Colombier 
126*83030dd5SDavid du Colombier static int
127*83030dd5SDavid du Colombier scale(int x)
128*83030dd5SDavid du Colombier {
129*83030dd5SDavid du Colombier 	int sign = 1;
130*83030dd5SDavid du Colombier 
131*83030dd5SDavid du Colombier 	if(x < 0){
132*83030dd5SDavid du Colombier 		sign = -1;
133*83030dd5SDavid du Colombier 		x = -x;
134*83030dd5SDavid du Colombier 	}
135*83030dd5SDavid du Colombier 	switch(x){
136*83030dd5SDavid du Colombier 	case 0:
137*83030dd5SDavid du Colombier 	case 1:
138*83030dd5SDavid du Colombier 	case 2:
139*83030dd5SDavid du Colombier 	case 3:
140*83030dd5SDavid du Colombier 		break;
141*83030dd5SDavid du Colombier 	case 4:
142*83030dd5SDavid du Colombier 		x = 6 + (accel>>2);
143*83030dd5SDavid du Colombier 		break;
144*83030dd5SDavid du Colombier 	case 5:
145*83030dd5SDavid du Colombier 		x = 9 + (accel>>1);
146*83030dd5SDavid du Colombier 		break;
147*83030dd5SDavid du Colombier 	default:
148*83030dd5SDavid du Colombier 		x *= MaxAcc;
149*83030dd5SDavid du Colombier 		break;
150*83030dd5SDavid du Colombier 	}
151*83030dd5SDavid du Colombier 	return sign*x;
152*83030dd5SDavid du Colombier }
153*83030dd5SDavid du Colombier 
154*83030dd5SDavid du Colombier void
155*83030dd5SDavid du Colombier ptrwork(void* a)
156*83030dd5SDavid du Colombier {
157*83030dd5SDavid du Colombier 	int x, y, b, c, mfd, ptrfd;
158*83030dd5SDavid du Colombier 	char	buf[32];
159*83030dd5SDavid du Colombier 	Dev*	f = a;
160*83030dd5SDavid du Colombier 	static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
161*83030dd5SDavid du Colombier 
162*83030dd5SDavid du Colombier 	ptrfd = f->fd;
163*83030dd5SDavid du Colombier 	if(ptrfd < 0)
164*83030dd5SDavid du Colombier 		return;
165*83030dd5SDavid du Colombier 	mfd = -1;
166*83030dd5SDavid du Colombier 	if(f->msz < 3 || f->msz > sizeof buf)
167*83030dd5SDavid du Colombier 		sysfatal("bug: ptrwork: bad mouse maxpkt");
168*83030dd5SDavid du Colombier 	if(!dryrun){
169*83030dd5SDavid du Colombier 		mfd = open("#m/mousein", OWRITE);
170*83030dd5SDavid du Colombier 		if(mfd < 0)
171*83030dd5SDavid du Colombier 			sysfatal("%s: mousein: %r", f->name);
172*83030dd5SDavid du Colombier 	}
173*83030dd5SDavid du Colombier 	for(;;){
174*83030dd5SDavid du Colombier 		memset(buf, 0, sizeof buf);
175*83030dd5SDavid du Colombier 		c = robustread(ptrfd, buf, f->msz);
176*83030dd5SDavid du Colombier 		if(c == 0)
177*83030dd5SDavid du Colombier 			fprint(2, "%s: %s: eof\n", argv0, f->name);
178*83030dd5SDavid du Colombier 		if(c < 0)
179*83030dd5SDavid du Colombier 			fprint(2, "%s: %s: read: %r\n", argv0, f->name);
180*83030dd5SDavid du Colombier 		if(c <= 0){
181*83030dd5SDavid du Colombier 			if(!dryrun)
182*83030dd5SDavid du Colombier 				close(mfd);
183*83030dd5SDavid du Colombier 			close(f->fd);
184*83030dd5SDavid du Colombier 			threadexits("read");
185*83030dd5SDavid du Colombier 		}
186*83030dd5SDavid du Colombier 		if(c < 3)
187*83030dd5SDavid du Colombier 			continue;
188*83030dd5SDavid du Colombier 		if(accel) {
189*83030dd5SDavid du Colombier 			x = scale(buf[1]);
190*83030dd5SDavid du Colombier 			y = scale(buf[2]);
191*83030dd5SDavid du Colombier 		} else {
192*83030dd5SDavid du Colombier 			x = buf[1];
193*83030dd5SDavid du Colombier 			y = buf[2];
194*83030dd5SDavid du Colombier 		}
195*83030dd5SDavid du Colombier 		b = maptab[buf[0] & 0x7];
196*83030dd5SDavid du Colombier 		if(c > 3 && buf[3] == 1)	/* up */
197*83030dd5SDavid du Colombier 			b |= 0x08;
198*83030dd5SDavid du Colombier 		if(c > 3 && buf[3] == -1)	/* down */
199*83030dd5SDavid du Colombier 			b |= 0x10;
200*83030dd5SDavid du Colombier 		if(hdebug)
201*83030dd5SDavid du Colombier 			fprint(2, "%s: %s: m%11d %11d %11d\n",
202*83030dd5SDavid du Colombier 				argv0, f->name, x, y, b);
203*83030dd5SDavid du Colombier 		if(!dryrun)
204*83030dd5SDavid du Colombier 			if(fprint(mfd, "m%11d %11d %11d", x, y, b) < 0){
205*83030dd5SDavid du Colombier 				fprint(2, "%s: #m/mousein: write: %r", argv0);
206*83030dd5SDavid du Colombier 				close(mfd);
207*83030dd5SDavid du Colombier 				close(f->fd);
208*83030dd5SDavid du Colombier 				threadexits("write");
209*83030dd5SDavid du Colombier 			}
210*83030dd5SDavid du Colombier 	}
211*83030dd5SDavid du Colombier }
212*83030dd5SDavid du Colombier 
213*83030dd5SDavid du Colombier static void
214*83030dd5SDavid du Colombier stoprepeat(void)
215*83030dd5SDavid du Colombier {
216*83030dd5SDavid du Colombier 	sendul(repeatc, Awakemsg);
217*83030dd5SDavid du Colombier }
218*83030dd5SDavid du Colombier 
219*83030dd5SDavid du Colombier static void
220*83030dd5SDavid du Colombier startrepeat(uchar esc1, uchar sc)
221*83030dd5SDavid du Colombier {
222*83030dd5SDavid du Colombier 	ulong c;
223*83030dd5SDavid du Colombier 
224*83030dd5SDavid du Colombier 	if(esc1)
225*83030dd5SDavid du Colombier 		c = SCesc1 << 8 | (sc & 0xff);
226*83030dd5SDavid du Colombier 	else
227*83030dd5SDavid du Colombier 		c = sc;
228*83030dd5SDavid du Colombier 	sendul(repeatc, c);
229*83030dd5SDavid du Colombier }
230*83030dd5SDavid du Colombier 
231*83030dd5SDavid du Colombier static int kbinfd = -1;
232*83030dd5SDavid du Colombier 
233*83030dd5SDavid du Colombier static void
234*83030dd5SDavid du Colombier putscan(uchar esc, uchar sc)
235*83030dd5SDavid du Colombier {
236*83030dd5SDavid du Colombier 	static uchar s[2] = {SCesc1, 0};
237*83030dd5SDavid du Colombier 
238*83030dd5SDavid du Colombier 	if(sc == 0x41){
239*83030dd5SDavid du Colombier 		hdebug = 1;
240*83030dd5SDavid du Colombier 		return;
241*83030dd5SDavid du Colombier 	}
242*83030dd5SDavid du Colombier 	if(sc == 0x42){
243*83030dd5SDavid du Colombier 		hdebug = 0;
244*83030dd5SDavid du Colombier 		return;
245*83030dd5SDavid du Colombier 	}
246*83030dd5SDavid du Colombier 	if(kbinfd < 0 && !dryrun)
247*83030dd5SDavid du Colombier 		kbinfd = open("#Ι/kbin", OWRITE);
248*83030dd5SDavid du Colombier 	if(kbinfd < 0 && !dryrun)
249*83030dd5SDavid du Colombier 		sysfatal("/dev/kbin: %r");
250*83030dd5SDavid du Colombier 	if(hdebug)
251*83030dd5SDavid du Colombier 		fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
252*83030dd5SDavid du Colombier 	if(!dryrun){
253*83030dd5SDavid du Colombier 			s[1] = sc;
254*83030dd5SDavid du Colombier 		if(esc && sc != 0)
255*83030dd5SDavid du Colombier 			write(kbinfd, s, 2);
256*83030dd5SDavid du Colombier 		else if(sc != 0)
257*83030dd5SDavid du Colombier 			write(kbinfd, s+1, 1);
258*83030dd5SDavid du Colombier }
259*83030dd5SDavid du Colombier }
260*83030dd5SDavid du Colombier 
261*83030dd5SDavid du Colombier static void
262*83030dd5SDavid du Colombier repeatproc(void*)
263*83030dd5SDavid du Colombier {
264*83030dd5SDavid du Colombier 	ulong l;
265*83030dd5SDavid du Colombier 	uchar esc1, sc;
266*83030dd5SDavid du Colombier 
267*83030dd5SDavid du Colombier 	assert(sizeof(int) <= sizeof(void*));
268*83030dd5SDavid du Colombier 
269*83030dd5SDavid du Colombier 	for(;;){
270*83030dd5SDavid du Colombier 		l = recvul(repeatc);		/*  wait for work*/
271*83030dd5SDavid du Colombier 		if(l == 0xdeaddead)
272*83030dd5SDavid du Colombier 			continue;
273*83030dd5SDavid du Colombier 		esc1 = l >> 8;
274*83030dd5SDavid du Colombier 		sc = l;
275*83030dd5SDavid du Colombier 		for(;;){
276*83030dd5SDavid du Colombier 			putscan(esc1, sc);
277*83030dd5SDavid du Colombier 			sleep(80);
278*83030dd5SDavid du Colombier 			l = nbrecvul(repeatc);
279*83030dd5SDavid du Colombier 			if(l != 0){		/*  stop repeating*/
280*83030dd5SDavid du Colombier 				if(l != Awakemsg)
281*83030dd5SDavid du Colombier 					fprint(2, "kb: race: should be awake mesg\n");
282*83030dd5SDavid du Colombier 				break;
283*83030dd5SDavid du Colombier 			}
284*83030dd5SDavid du Colombier 		}
285*83030dd5SDavid du Colombier 	}
286*83030dd5SDavid du Colombier }
287*83030dd5SDavid du Colombier 
288*83030dd5SDavid du Colombier 
289*83030dd5SDavid du Colombier #define hasesc1(sc)	(((sc) > 0x47) || ((sc) == 0x38))
290*83030dd5SDavid du Colombier 
291*83030dd5SDavid du Colombier static void
292*83030dd5SDavid du Colombier putmod(uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
293*83030dd5SDavid du Colombier {
294*83030dd5SDavid du Colombier 	if((mods&mask) && !(omods&mask))
295*83030dd5SDavid du Colombier 		putscan(esc, sc);
296*83030dd5SDavid du Colombier 	if(!(mods&mask) && (omods&mask))
297*83030dd5SDavid du Colombier 		putscan(esc, Keyup|sc);
298*83030dd5SDavid du Colombier }
299*83030dd5SDavid du Colombier 
300*83030dd5SDavid du Colombier /*
301*83030dd5SDavid du Colombier  * This routine diffs the state with the last known state
302*83030dd5SDavid du Colombier  * and invents the scan codes that would have been sent
303*83030dd5SDavid du Colombier  * by a non-usb keyboard in that case. This also requires supplying
304*83030dd5SDavid du Colombier  * the extra esc1 byte as well as keyup flags.
305*83030dd5SDavid du Colombier  * The aim is to allow future addition of other keycode pages
306*83030dd5SDavid du Colombier  * for other keyboards.
307*83030dd5SDavid du Colombier  */
308*83030dd5SDavid du Colombier static void
309*83030dd5SDavid du Colombier putkeys(uchar buf[], uchar obuf[], int n)
310*83030dd5SDavid du Colombier {
311*83030dd5SDavid du Colombier 	int i, j;
312*83030dd5SDavid du Colombier 	uchar sc;
313*83030dd5SDavid du Colombier 	static int repeating = 0, times = 0;
314*83030dd5SDavid du Colombier 	static uchar last = 0;
315*83030dd5SDavid du Colombier 
316*83030dd5SDavid du Colombier 	putmod(buf[0], obuf[0], Mctrl, 0, SCctrl);
317*83030dd5SDavid du Colombier 	putmod(buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
318*83030dd5SDavid du Colombier 	putmod(buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
319*83030dd5SDavid du Colombier 	putmod(buf[0], obuf[0], Mcompose, 0, SCcompose);
320*83030dd5SDavid du Colombier 	putmod(buf[0], obuf[0], Maltgr, 1, SCcompose);
321*83030dd5SDavid du Colombier 
322*83030dd5SDavid du Colombier 	/*
323*83030dd5SDavid du Colombier 	 * If we get three times the same (single) key, we start
324*83030dd5SDavid du Colombier 	 * repeating. Otherwise, we stop repeating and
325*83030dd5SDavid du Colombier 	 * perform normal processing.
326*83030dd5SDavid du Colombier 	 */
327*83030dd5SDavid du Colombier 	if(buf[2] != 0 && buf[3] == 0 && buf[2] == last){
328*83030dd5SDavid du Colombier 		if(repeating)
329*83030dd5SDavid du Colombier 			return;	/* already being done */
330*83030dd5SDavid du Colombier 		times++;
331*83030dd5SDavid du Colombier 		if(times >= 2){
332*83030dd5SDavid du Colombier 			repeating = 1;
333*83030dd5SDavid du Colombier 			sc = sctab[buf[2]];
334*83030dd5SDavid du Colombier 			startrepeat(hasesc1(sc), sc);
335*83030dd5SDavid du Colombier 			return;
336*83030dd5SDavid du Colombier 		}
337*83030dd5SDavid du Colombier 	} else
338*83030dd5SDavid du Colombier 		times = 0;
339*83030dd5SDavid du Colombier 	last = buf[2];
340*83030dd5SDavid du Colombier 	if(repeating){
341*83030dd5SDavid du Colombier 		repeating = 0;
342*83030dd5SDavid du Colombier 		stoprepeat();
343*83030dd5SDavid du Colombier 	}
344*83030dd5SDavid du Colombier 
345*83030dd5SDavid du Colombier 	/* Report key downs */
346*83030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
347*83030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
348*83030dd5SDavid du Colombier 			if(buf[i] == obuf[j])
349*83030dd5SDavid du Colombier 			 	break;
350*83030dd5SDavid du Colombier 		if(j == n && buf[i] != 0){
351*83030dd5SDavid du Colombier 			sc = sctab[buf[i]];
352*83030dd5SDavid du Colombier 			putscan(hasesc1(sc), sc);
353*83030dd5SDavid du Colombier 		}
354*83030dd5SDavid du Colombier 	}
355*83030dd5SDavid du Colombier 
356*83030dd5SDavid du Colombier 	/* Report key ups */
357*83030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
358*83030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
359*83030dd5SDavid du Colombier 			if(obuf[i] == buf[j])
360*83030dd5SDavid du Colombier 				break;
361*83030dd5SDavid du Colombier 		if(j == n && obuf[i] != 0){
362*83030dd5SDavid du Colombier 			sc = sctab[obuf[i]];
363*83030dd5SDavid du Colombier 			putscan(hasesc1(sc), sc|Keyup);
364*83030dd5SDavid du Colombier 		}
365*83030dd5SDavid du Colombier 	}
366*83030dd5SDavid du Colombier }
367*83030dd5SDavid du Colombier 
368*83030dd5SDavid du Colombier static int
369*83030dd5SDavid du Colombier kbdbusy(uchar* buf, int n)
370*83030dd5SDavid du Colombier {
371*83030dd5SDavid du Colombier 	int i;
372*83030dd5SDavid du Colombier 
373*83030dd5SDavid du Colombier 	for(i = 1; i < n; i++)
374*83030dd5SDavid du Colombier 		if(buf[i] == 0 || buf[i] != buf[0])
375*83030dd5SDavid du Colombier 			return 0;
376*83030dd5SDavid du Colombier 	return 1;
377*83030dd5SDavid du Colombier }
378*83030dd5SDavid du Colombier 
379*83030dd5SDavid du Colombier void
380*83030dd5SDavid du Colombier kbdwork(void *a)
381*83030dd5SDavid du Colombier {
382*83030dd5SDavid du Colombier 	int c, i, kbdfd;
383*83030dd5SDavid du Colombier 	uchar buf[64], lbuf[64];
384*83030dd5SDavid du Colombier 	Dev *f = a;
385*83030dd5SDavid du Colombier 
386*83030dd5SDavid du Colombier 	kbdfd = f->fd;
387*83030dd5SDavid du Colombier 	if(kbdfd < 0)
388*83030dd5SDavid du Colombier 		return;
389*83030dd5SDavid du Colombier 	if(f->msz < 3 || f->msz > sizeof buf)
390*83030dd5SDavid du Colombier 		sysfatal("bug: ptrwork: bad kbd maxpkt");
391*83030dd5SDavid du Colombier 	repeatc = chancreate(sizeof(ulong), 0);
392*83030dd5SDavid du Colombier 	if(repeatc == nil)
393*83030dd5SDavid du Colombier 		sysfatal("repeat chan: %r");
394*83030dd5SDavid du Colombier 	proccreate(repeatproc, nil, 32*1024);
395*83030dd5SDavid du Colombier 	memset(lbuf, 0, sizeof lbuf);
396*83030dd5SDavid du Colombier 	for(;;) {
397*83030dd5SDavid du Colombier 		memset(buf, 0, sizeof buf);
398*83030dd5SDavid du Colombier 		c = robustread(kbdfd, buf, f->msz);
399*83030dd5SDavid du Colombier 		if(c == 0)
400*83030dd5SDavid du Colombier 			fprint(2, "%s: %s: eof\n", argv0, f->name);
401*83030dd5SDavid du Colombier 		if(c < 0)
402*83030dd5SDavid du Colombier 			fprint(2, "%s: %s: read: %r\n", argv0, f->name);
403*83030dd5SDavid du Colombier 		if(c <= 0){
404*83030dd5SDavid du Colombier 			if(!dryrun)
405*83030dd5SDavid du Colombier 				close(kbinfd);
406*83030dd5SDavid du Colombier 			close(f->fd);
407*83030dd5SDavid du Colombier 			threadexits("read");
408*83030dd5SDavid du Colombier 		}
409*83030dd5SDavid du Colombier 		if(c < 3)
410*83030dd5SDavid du Colombier 			continue;
411*83030dd5SDavid du Colombier 		if(kbdbusy(buf + 2, c - 2))
412*83030dd5SDavid du Colombier 			continue;
413*83030dd5SDavid du Colombier 		if(hdebug > 1){
414*83030dd5SDavid du Colombier 			fprint(2, "kbd mod %x: ", buf[0]);
415*83030dd5SDavid du Colombier 			for(i = 2; i < c; i++)
416*83030dd5SDavid du Colombier 				fprint(2, "kc %x ", buf[i]);
417*83030dd5SDavid du Colombier 			fprint(2, "\n");
418*83030dd5SDavid du Colombier 		}
419*83030dd5SDavid du Colombier 		putkeys(buf, lbuf, f->msz);
420*83030dd5SDavid du Colombier 		memmove(lbuf, buf, c);
421*83030dd5SDavid du Colombier 	}
422*83030dd5SDavid du Colombier }
423*83030dd5SDavid du Colombier 
424*83030dd5SDavid du Colombier static int
425*83030dd5SDavid du Colombier probeif(Dev* f, int ctlrno, int i, int kcsp, int csp, int, int)
426*83030dd5SDavid du Colombier {
427*83030dd5SDavid du Colombier 	int found, n, sfd;
428*83030dd5SDavid du Colombier 	char buf[256], cspstr[50], kcspstr[50];
429*83030dd5SDavid du Colombier 
430*83030dd5SDavid du Colombier 	seprint(cspstr, cspstr + sizeof cspstr, "0x%0.06x", csp);
431*83030dd5SDavid du Colombier 	seprint(buf, buf + sizeof buf, "/dev/usb%d/%d/status", ctlrno, i);
432*83030dd5SDavid du Colombier 	sfd = open(buf, OREAD);
433*83030dd5SDavid du Colombier 	if(sfd < 0)
434*83030dd5SDavid du Colombier 		return -1;
435*83030dd5SDavid du Colombier 	n = read(sfd, buf, sizeof buf - 1);
436*83030dd5SDavid du Colombier 	if(n <= 0){
437*83030dd5SDavid du Colombier 		close(sfd);
438*83030dd5SDavid du Colombier 		return -1;
439*83030dd5SDavid du Colombier 	}
440*83030dd5SDavid du Colombier 	buf[n] = 0;
441*83030dd5SDavid du Colombier 	close(sfd);
442*83030dd5SDavid du Colombier 	/*
443*83030dd5SDavid du Colombier 	 * Mouse + keyboard combos may report the interface as Kbdcsp,
444*83030dd5SDavid du Colombier 	 * because it's the endpoint the one with the right csp.
445*83030dd5SDavid du Colombier 	 */
446*83030dd5SDavid du Colombier 	sprint(cspstr, "Enabled 0x%0.06x", csp);
447*83030dd5SDavid du Colombier 	found = (strncmp(buf, cspstr, strlen(cspstr)) == 0);
448*83030dd5SDavid du Colombier 	if(!found){
449*83030dd5SDavid du Colombier 		sprint(cspstr, " 0x%0.06x", csp);
450*83030dd5SDavid du Colombier 		sprint(kcspstr, "Enabled 0x%0.06x", kcsp);
451*83030dd5SDavid du Colombier 		if(strncmp(buf, kcspstr, strlen(kcspstr)) == 0 &&
452*83030dd5SDavid du Colombier 		   strstr(buf, cspstr) != nil)
453*83030dd5SDavid du Colombier 			found = 1;
454*83030dd5SDavid du Colombier 	}
455*83030dd5SDavid du Colombier 	if(found){
456*83030dd5SDavid du Colombier 		f->ctlrno = ctlrno;
457*83030dd5SDavid du Colombier 		f->id = i;
458*83030dd5SDavid du Colombier 		f->enabled = 1;
459*83030dd5SDavid du Colombier 		if(hdebug)
460*83030dd5SDavid du Colombier 			fprint(2, "%s: csp 0x%x at /dev/usb%d/%d\n",
461*83030dd5SDavid du Colombier 				f->name, csp, f->ctlrno, f->id);
462*83030dd5SDavid du Colombier 		return 0;
463*83030dd5SDavid du Colombier 	}
464*83030dd5SDavid du Colombier 	if(hdebug)
465*83030dd5SDavid du Colombier 		fprint(2, "%s: not found %s\n", f->name, cspstr);
466*83030dd5SDavid du Colombier 	return -1;
467*83030dd5SDavid du Colombier 
468*83030dd5SDavid du Colombier }
469*83030dd5SDavid du Colombier 
470*83030dd5SDavid du Colombier static int
471*83030dd5SDavid du Colombier probedev(Dev* f, int kcsp, int csp, int vid, int did)
472*83030dd5SDavid du Colombier {
473*83030dd5SDavid du Colombier 	int c, i;
474*83030dd5SDavid du Colombier 
475*83030dd5SDavid du Colombier 	for(c = 0; c < 16; c++)
476*83030dd5SDavid du Colombier 		if(f->ctlrno == 0 || c == f->ctlrno)
477*83030dd5SDavid du Colombier 			for(i = 1; i < 128; i++)
478*83030dd5SDavid du Colombier 				if(f->id == 0 || i == f->id)
479*83030dd5SDavid du Colombier 				if(probeif(f, c, i, kcsp, csp, vid, did) != -1){
480*83030dd5SDavid du Colombier 					f->csp = csp;
481*83030dd5SDavid du Colombier 					return 0;
482*83030dd5SDavid du Colombier 				}
483*83030dd5SDavid du Colombier 	f->enabled = 0;
484*83030dd5SDavid du Colombier 	if(hdebug || verbose)
485*83030dd5SDavid du Colombier 		fprint(2, "%s: csp 0x%x: not found\n", f->name, csp);
486*83030dd5SDavid du Colombier 	return -1;
487*83030dd5SDavid du Colombier }
488*83030dd5SDavid du Colombier 
489*83030dd5SDavid du Colombier static void
490*83030dd5SDavid du Colombier initdev(Dev* f)
491*83030dd5SDavid du Colombier {
492*83030dd5SDavid du Colombier 	int i;
493*83030dd5SDavid du Colombier 
494*83030dd5SDavid du Colombier 	f->dev = opendev(f->ctlrno, f->id);
495*83030dd5SDavid du Colombier 	if(f->dev == nil || describedevice(f->dev) < 0) {
496*83030dd5SDavid du Colombier 		fprint(2, "init failed: %s: %r\n", f->name);
497*83030dd5SDavid du Colombier 		f->enabled = 0;
498*83030dd5SDavid du Colombier 		f->ctlrno = f->id = -1;
499*83030dd5SDavid du Colombier 		return;
500*83030dd5SDavid du Colombier 	}
501*83030dd5SDavid du Colombier 	memset(f->dev->config, 0, sizeof f->dev->config);
502*83030dd5SDavid du Colombier 	for(i = 0; i < f->dev->nconf; i++){
503*83030dd5SDavid du Colombier 		f->dev->config[i] = mallocz(sizeof *f->dev->config[i], 1);
504*83030dd5SDavid du Colombier 		loadconfig(f->dev, i);
505*83030dd5SDavid du Colombier 	}
506*83030dd5SDavid du Colombier 	if(verbose || hdebug){
507*83030dd5SDavid du Colombier 		fprint(2, "%s found: ctlrno=%d id=%d\n",
508*83030dd5SDavid du Colombier 			f->name, f->ctlrno, f->id);
509*83030dd5SDavid du Colombier //		printdevice(f->dev);	// TODO
510*83030dd5SDavid du Colombier 	}
511*83030dd5SDavid du Colombier }
512*83030dd5SDavid du Colombier 
513*83030dd5SDavid du Colombier static int
514*83030dd5SDavid du Colombier setbootproto(Dev* f)
515*83030dd5SDavid du Colombier {
516*83030dd5SDavid du Colombier 	int r, id;
517*83030dd5SDavid du Colombier 	Endpt* ep;
518*83030dd5SDavid du Colombier 
519*83030dd5SDavid du Colombier 	ep = f->dev->ep[0];
520*83030dd5SDavid du Colombier 	r = RH2D | Rclass | Rinterface;
521*83030dd5SDavid du Colombier 	id = f->dev->ep[f->ep]->iface->interface;
522*83030dd5SDavid du Colombier 	return setupreq(ep, r, SET_PROTO, BOOT_PROTO, id, 0);
523*83030dd5SDavid du Colombier }
524*83030dd5SDavid du Colombier 
525*83030dd5SDavid du Colombier static void
526*83030dd5SDavid du Colombier startdev(Dev* f)
527*83030dd5SDavid du Colombier {
528*83030dd5SDavid du Colombier 	int i;
529*83030dd5SDavid du Colombier 	char buf[128];
530*83030dd5SDavid du Colombier 	Endpt* ep;
531*83030dd5SDavid du Colombier 
532*83030dd5SDavid du Colombier 	f->ep = -1;
533*83030dd5SDavid du Colombier 	ep = nil;
534*83030dd5SDavid du Colombier 	for(i = 0; i < Nendpt; i++)
535*83030dd5SDavid du Colombier 		if((ep = f->dev->ep[i]) != nil &&
536*83030dd5SDavid du Colombier 		    ep->csp == f->csp && ep->type == Eintr && ep->dir == Ein){
537*83030dd5SDavid du Colombier 			f->ep = i;
538*83030dd5SDavid du Colombier 			f->msz = ep->maxpkt;
539*83030dd5SDavid du Colombier 			break;
540*83030dd5SDavid du Colombier 		}
541*83030dd5SDavid du Colombier 	if(ep == nil){
542*83030dd5SDavid du Colombier 		fprint(2, "%s: %s: bug: no endpoint\n", argv0, f->name);
543*83030dd5SDavid du Colombier 		return;
544*83030dd5SDavid du Colombier 	}
545*83030dd5SDavid du Colombier 	sprint(buf, "ep %d 10 r %d", f->ep, f->msz);
546*83030dd5SDavid du Colombier 	if(hdebug)
547*83030dd5SDavid du Colombier 		fprint(2, "%s: %s: ep %d: ctl %s\n", argv0, f->name, f->ep, buf);
548*83030dd5SDavid du Colombier 	if(write(f->dev->ctl, buf, strlen(buf)) != strlen(buf)){
549*83030dd5SDavid du Colombier 		fprint(2, "%s: %s: startdev: %r\n", argv0, f->name);
550*83030dd5SDavid du Colombier 		return;
551*83030dd5SDavid du Colombier 	}
552*83030dd5SDavid du Colombier 	sprint(buf, "/dev/usb%d/%d/ep%ddata", f->ctlrno, f->id, f->ep);
553*83030dd5SDavid du Colombier 	f->fd = open(buf, OREAD);
554*83030dd5SDavid du Colombier 	if(f->fd < 0){
555*83030dd5SDavid du Colombier 		fprint(2, "%s: opening %s: %s: %r", argv0, f->name, buf);
556*83030dd5SDavid du Colombier 		return;
557*83030dd5SDavid du Colombier 	}
558*83030dd5SDavid du Colombier 	if(setbootproto(f) < 0)
559*83030dd5SDavid du Colombier 		fprint(2, "%s: %s: setbootproto: %r\n", argv0, f->name);
560*83030dd5SDavid du Colombier 	if(hdebug)
561*83030dd5SDavid du Colombier 		fprint(2, "starting %s\n", f->name);
562*83030dd5SDavid du Colombier 	proccreate(f->proc, f, 32*1024);
563*83030dd5SDavid du Colombier }
564*83030dd5SDavid du Colombier 
565*83030dd5SDavid du Colombier static void
566*83030dd5SDavid du Colombier usage(void)
567*83030dd5SDavid du Colombier {
568*83030dd5SDavid du Colombier 	fprint(2, "usage: %s [-dkmn] [-a n] [ctlrno usbport]\n", argv0);
569*83030dd5SDavid du Colombier 	threadexitsall("usage");
570*83030dd5SDavid du Colombier }
571*83030dd5SDavid du Colombier 
572*83030dd5SDavid du Colombier void
573*83030dd5SDavid du Colombier threadmain(int argc, char **argv)
574*83030dd5SDavid du Colombier {
575*83030dd5SDavid du Colombier 
576*83030dd5SDavid du Colombier 	quotefmtinstall();
577*83030dd5SDavid du Colombier 	usbfmtinit();
578*83030dd5SDavid du Colombier 
579*83030dd5SDavid du Colombier 	pf.enabled = kf.enabled = 1;
580*83030dd5SDavid du Colombier 	ARGBEGIN{
581*83030dd5SDavid du Colombier 	case 'a':
582*83030dd5SDavid du Colombier 		accel = strtol(EARGF(usage()), nil, 0);
583*83030dd5SDavid du Colombier 		break;
584*83030dd5SDavid du Colombier 	case 'd':
585*83030dd5SDavid du Colombier 		hdebug++;
586*83030dd5SDavid du Colombier 		usbdebug++;
587*83030dd5SDavid du Colombier 		break;
588*83030dd5SDavid du Colombier 	case 'k':
589*83030dd5SDavid du Colombier 		kf.enabled = 1;
590*83030dd5SDavid du Colombier 		pf.enabled = 0;
591*83030dd5SDavid du Colombier 		break;
592*83030dd5SDavid du Colombier 	case 'm':
593*83030dd5SDavid du Colombier 		kf.enabled = 0;
594*83030dd5SDavid du Colombier 		pf.enabled = 1;
595*83030dd5SDavid du Colombier 		break;
596*83030dd5SDavid du Colombier 	case 'n':
597*83030dd5SDavid du Colombier 		dryrun = 1;
598*83030dd5SDavid du Colombier 		break;
599*83030dd5SDavid du Colombier 	default:
600*83030dd5SDavid du Colombier 		usage();
601*83030dd5SDavid du Colombier 	}ARGEND;
602*83030dd5SDavid du Colombier 
603*83030dd5SDavid du Colombier 	switch(argc){
604*83030dd5SDavid du Colombier 	case 0:
605*83030dd5SDavid du Colombier 		break;
606*83030dd5SDavid du Colombier 	case 2:
607*83030dd5SDavid du Colombier 		pf.ctlrno = kf.ctlrno = atoi(argv[0]);
608*83030dd5SDavid du Colombier 		pf.id = kf.id = atoi(argv[1]);
609*83030dd5SDavid du Colombier 		break;
610*83030dd5SDavid du Colombier 	default:
611*83030dd5SDavid du Colombier 		usage();
612*83030dd5SDavid du Colombier 	}
613*83030dd5SDavid du Colombier 	threadnotify(robusthandler, 1);
614*83030dd5SDavid du Colombier 
615*83030dd5SDavid du Colombier 	kf.name = "kbd";
616*83030dd5SDavid du Colombier 	kf.proc = kbdwork;
617*83030dd5SDavid du Colombier 	pf.name = "mouse";
618*83030dd5SDavid du Colombier 	pf.proc = ptrwork;
619*83030dd5SDavid du Colombier 
620*83030dd5SDavid du Colombier 	if(kf.enabled)
621*83030dd5SDavid du Colombier 		probedev(&kf, KbdCSP, KbdCSP, 0, 0);
622*83030dd5SDavid du Colombier 	if(kf.enabled)
623*83030dd5SDavid du Colombier 		initdev(&kf);
624*83030dd5SDavid du Colombier 	if(pf.enabled)
625*83030dd5SDavid du Colombier 		probedev(&pf, KbdCSP, PtrCSP, 0, 0);
626*83030dd5SDavid du Colombier 	if(pf.enabled)
627*83030dd5SDavid du Colombier 		if(kf.enabled && pf.ctlrno == kf.ctlrno && pf.id == kf.id)
628*83030dd5SDavid du Colombier 			pf.dev = kf.dev;
629*83030dd5SDavid du Colombier 		else
630*83030dd5SDavid du Colombier 			initdev(&pf);
631*83030dd5SDavid du Colombier 	rfork(RFNOTEG);
632*83030dd5SDavid du Colombier 	if(kf.enabled)
633*83030dd5SDavid du Colombier 		startdev(&kf);
634*83030dd5SDavid du Colombier 	if(pf.enabled)
635*83030dd5SDavid du Colombier 		startdev(&pf);
636*83030dd5SDavid du Colombier 	threadexits(nil);
637*83030dd5SDavid du Colombier }
638