xref: /plan9/sys/src/cmd/usb/kb/kb.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
183030dd5SDavid du Colombier /*
283030dd5SDavid du Colombier  * USB Human Interaction Device: keyboard and mouse.
383030dd5SDavid du Colombier  *
483030dd5SDavid du Colombier  * If there's no usb keyboard, it tries to setup the mouse, if any.
583030dd5SDavid du Colombier  * It should be started at boot time.
683030dd5SDavid du Colombier  *
7d40255d8SDavid du Colombier  * Mouse events are converted to the format of mouse(3)'s mousein file.
883030dd5SDavid du Colombier  * Keyboard keycodes are translated to scan codes and sent to kbin(3).
983030dd5SDavid du Colombier  *
10d40255d8SDavid du Colombier  * If there is no keyboard, it tries to setup the mouse properly, else it falls
11d40255d8SDavid du Colombier  * back to boot protocol.
1283030dd5SDavid du Colombier  */
1383030dd5SDavid du Colombier 
1483030dd5SDavid du Colombier #include <u.h>
1583030dd5SDavid du Colombier #include <libc.h>
1683030dd5SDavid du Colombier #include <thread.h>
1783030dd5SDavid du Colombier #include "usb.h"
1883030dd5SDavid du Colombier #include "hid.h"
1983030dd5SDavid du Colombier 
20906943f9SDavid du Colombier enum
21906943f9SDavid du Colombier {
22906943f9SDavid du Colombier 	Awakemsg= 0xdeaddead,
23906943f9SDavid du Colombier 	Diemsg	= 0xbeefbeef,
240cc6832dSDavid du Colombier 	Dwcidle	= 8,
25906943f9SDavid du Colombier };
26906943f9SDavid du Colombier 
27906943f9SDavid du Colombier typedef struct KDev KDev;
28906943f9SDavid du Colombier typedef struct Kin Kin;
29906943f9SDavid du Colombier 
30906943f9SDavid du Colombier struct KDev
31906943f9SDavid du Colombier {
32906943f9SDavid du Colombier 	Dev*	dev;		/* usb device*/
33906943f9SDavid du Colombier 	Dev*	ep;		/* endpoint to get events */
34906943f9SDavid du Colombier 	Kin*	in;		/* used to send events to kernel */
350cc6832dSDavid du Colombier 	int	idle;		/* min time between reports (× 4ms) */
36906943f9SDavid du Colombier 	Channel*repeatc;	/* only for keyboard */
37906943f9SDavid du Colombier 	int	accel;		/* only for mouse */
38d40255d8SDavid du Colombier 	int	bootp;		/* has associated keyboard */
399b7bf7dfSDavid du Colombier 	int	debug;
40d40255d8SDavid du Colombier 	HidRepTempl templ;
41d40255d8SDavid du Colombier 	int	(*ptrvals)(KDev *kd, Chain *ch, int *px, int *py, int *pb);
42906943f9SDavid du Colombier };
43906943f9SDavid du Colombier 
44906943f9SDavid du Colombier /*
45906943f9SDavid du Colombier  * Kbdin and mousein files must be shared among all instances.
46906943f9SDavid du Colombier  */
47906943f9SDavid du Colombier struct Kin
48906943f9SDavid du Colombier {
49906943f9SDavid du Colombier 	int	ref;
50906943f9SDavid du Colombier 	int	fd;
51906943f9SDavid du Colombier 	char*	name;
52906943f9SDavid du Colombier };
53906943f9SDavid du Colombier 
5483030dd5SDavid du Colombier /*
5583030dd5SDavid du Colombier  * Map for the logitech bluetooth mouse with 8 buttons and wheels.
5683030dd5SDavid du Colombier  *	{ ptr ->mouse}
5783030dd5SDavid du Colombier  *	{ 0x01, 0x01 },	// left
5883030dd5SDavid du Colombier  *	{ 0x04, 0x02 },	// middle
5983030dd5SDavid du Colombier  *	{ 0x02, 0x04 },	// right
6083030dd5SDavid du Colombier  *	{ 0x40, 0x08 },	// up
6183030dd5SDavid du Colombier  *	{ 0x80, 0x10 },	// down
6283030dd5SDavid du Colombier  *	{ 0x10, 0x08 },	// side up
6383030dd5SDavid du Colombier  *	{ 0x08, 0x10 },	// side down
6483030dd5SDavid du Colombier  *	{ 0x20, 0x02 }, // page
6583030dd5SDavid du Colombier  * besides wheel and regular up/down report the 4th byte as 1/-1
6683030dd5SDavid du Colombier  */
6783030dd5SDavid du Colombier 
6883030dd5SDavid du Colombier /*
6983030dd5SDavid du Colombier  * key code to scan code; for the page table used by
7083030dd5SDavid du Colombier  * the logitech bluetooth keyboard.
7183030dd5SDavid du Colombier  */
72906943f9SDavid du Colombier static char sctab[256] =
7383030dd5SDavid du Colombier {
7483030dd5SDavid du Colombier [0x00]	0x0,	0x0,	0x0,	0x0,	0x1e,	0x30,	0x2e,	0x20,
7583030dd5SDavid du Colombier [0x08]	0x12,	0x21,	0x22,	0x23,	0x17,	0x24,	0x25,	0x26,
7683030dd5SDavid du Colombier [0x10]	0x32,	0x31,	0x18,	0x19,	0x10,	0x13,	0x1f,	0x14,
7783030dd5SDavid du Colombier [0x18]	0x16,	0x2f,	0x11,	0x2d,	0x15,	0x2c,	0x2,	0x3,
7883030dd5SDavid du Colombier [0x20]	0x4,	0x5,	0x6,	0x7,	0x8,	0x9,	0xa,	0xb,
7983030dd5SDavid du Colombier [0x28]	0x1c,	0x1,	0xe,	0xf,	0x39,	0xc,	0xd,	0x1a,
8083030dd5SDavid du Colombier [0x30]	0x1b,	0x2b,	0x2b,	0x27,	0x28,	0x29,	0x33,	0x34,
8183030dd5SDavid du Colombier [0x38]	0x35,	0x3a,	0x3b,	0x3c,	0x3d,	0x3e,	0x3f,	0x40,
8283030dd5SDavid du Colombier [0x40]	0x41,	0x42,	0x43,	0x44,	0x57,	0x58,	0x63,	0x46,
8383030dd5SDavid du Colombier [0x48]	0x77,	0x52,	0x47,	0x49,	0x53,	0x4f,	0x51,	0x4d,
8483030dd5SDavid du Colombier [0x50]	0x4b,	0x50,	0x48,	0x45,	0x35,	0x37,	0x4a,	0x4e,
8583030dd5SDavid du Colombier [0x58]	0x1c,	0x4f,	0x50,	0x51,	0x4b,	0x4c,	0x4d,	0x47,
8683030dd5SDavid du Colombier [0x60]	0x48,	0x49,	0x52,	0x53,	0x56,	0x7f,	0x74,	0x75,
8783030dd5SDavid du Colombier [0x68]	0x55,	0x59,	0x5a,	0x5b,	0x5c,	0x5d,	0x5e,	0x5f,
8883030dd5SDavid du Colombier [0x70]	0x78,	0x79,	0x7a,	0x7b,	0x0,	0x0,	0x0,	0x0,
8983030dd5SDavid du Colombier [0x78]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x71,
9083030dd5SDavid du Colombier [0x80]	0x73,	0x72,	0x0,	0x0,	0x0,	0x7c,	0x0,	0x0,
9183030dd5SDavid du Colombier [0x88]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9283030dd5SDavid du Colombier [0x90]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9383030dd5SDavid du Colombier [0x98]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9483030dd5SDavid du Colombier [0xa0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9583030dd5SDavid du Colombier [0xa8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9683030dd5SDavid du Colombier [0xb0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9783030dd5SDavid du Colombier [0xb8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9883030dd5SDavid du Colombier [0xc0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
9983030dd5SDavid du Colombier [0xc8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
10083030dd5SDavid du Colombier [0xd0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
10183030dd5SDavid du Colombier [0xd8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
10283030dd5SDavid du Colombier [0xe0]	0x1d,	0x2a,	0x38,	0x7d,	0x61,	0x36,	0x64,	0x7e,
10383030dd5SDavid du Colombier [0xe8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x73,	0x72,	0x71,
10483030dd5SDavid du Colombier [0xf0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
10583030dd5SDavid du Colombier [0xf8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
10683030dd5SDavid du Colombier };
10783030dd5SDavid du Colombier 
108906943f9SDavid du Colombier static QLock inlck;
109906943f9SDavid du Colombier static Kin kbdin =
110906943f9SDavid du Colombier {
111906943f9SDavid du Colombier 	.ref = 0,
112906943f9SDavid du Colombier 	.name = "#Ι/kbin",
113906943f9SDavid du Colombier 	.fd = -1,
114906943f9SDavid du Colombier };
115906943f9SDavid du Colombier static Kin ptrin =
116906943f9SDavid du Colombier {
117906943f9SDavid du Colombier 	.ref = 0,
118906943f9SDavid du Colombier 	.name = "#m/mousein",
119906943f9SDavid du Colombier 	.fd = -1,
12083030dd5SDavid du Colombier };
12183030dd5SDavid du Colombier 
122d40255d8SDavid du Colombier static int ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb);
123d40255d8SDavid du Colombier static int ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb);
124d40255d8SDavid du Colombier 
125d5789509SDavid du Colombier static int
setbootproto(KDev * f,int eid,uchar *,int)126d40255d8SDavid du Colombier setbootproto(KDev* f, int eid, uchar *, int)
127d5789509SDavid du Colombier {
1280cc6832dSDavid du Colombier 	int nr, r, id;
129d5789509SDavid du Colombier 
130d40255d8SDavid du Colombier 	f->ptrvals = ptrbootpvals;
131d5789509SDavid du Colombier 	r = Rh2d|Rclass|Riface;
132d40255d8SDavid du Colombier 	dprint(2, "setting boot protocol\n");
133d5789509SDavid du Colombier 	id = f->dev->usb->ep[eid]->iface->id;
1340cc6832dSDavid du Colombier 	nr = usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
1350cc6832dSDavid du Colombier 	if(nr < 0)
1360cc6832dSDavid du Colombier 		return -1;
1370cc6832dSDavid du Colombier 	usbcmd(f->dev, r, Setidle, f->idle<<8, id, nil, 0);
1380cc6832dSDavid du Colombier 	return nr;
139d5789509SDavid du Colombier }
140d5789509SDavid du Colombier 
141d40255d8SDavid du Colombier static uchar ignoredesc[128];
142d40255d8SDavid du Colombier 
143d40255d8SDavid du Colombier static int
setfirstconfig(KDev * f,int eid,uchar * desc,int descsz)144d40255d8SDavid du Colombier setfirstconfig(KDev* f, int eid, uchar *desc, int descsz)
145d40255d8SDavid du Colombier {
146d40255d8SDavid du Colombier 	int nr, r, id, i;
147d40255d8SDavid du Colombier 
148d40255d8SDavid du Colombier 	dprint(2, "setting first config\n");
149d40255d8SDavid du Colombier 	if(desc == nil){
150d40255d8SDavid du Colombier 		descsz = sizeof ignoredesc;
151d40255d8SDavid du Colombier 		desc = ignoredesc;
152d40255d8SDavid du Colombier 	}
153d40255d8SDavid du Colombier 	id = f->dev->usb->ep[eid]->iface->id;
154d40255d8SDavid du Colombier 	r = Rh2d | Rstd | Rdev;
1559b7bf7dfSDavid du Colombier 	nr = usbcmd(f->dev,  r, Rsetconf, 1, 0, nil, 0);
156d40255d8SDavid du Colombier 	if(nr < 0)
157d40255d8SDavid du Colombier 		return -1;
158d40255d8SDavid du Colombier 	r = Rh2d | Rclass | Riface;
1590cc6832dSDavid du Colombier 	nr = usbcmd(f->dev, r, Setidle, f->idle<<8, id, nil, 0);
160d40255d8SDavid du Colombier 	if(nr < 0)
161d40255d8SDavid du Colombier 		return -1;
162d40255d8SDavid du Colombier 	r = Rd2h | Rstd | Riface;
163d40255d8SDavid du Colombier 	nr=usbcmd(f->dev,  r, Rgetdesc, Dreport<<8, id, desc, descsz);
1649b7bf7dfSDavid du Colombier 	if(nr <= 0)
165d40255d8SDavid du Colombier 		return -1;
1669b7bf7dfSDavid du Colombier 	if(f->debug){
167d40255d8SDavid du Colombier 		fprint(2, "report descriptor:");
168d40255d8SDavid du Colombier 		for(i = 0; i < nr; i++){
169eb2d6162SDavid du Colombier 			if(i%8 == 0)
170eb2d6162SDavid du Colombier 				fprint(2, "\n\t");
171d40255d8SDavid du Colombier 			fprint(2, "%#2.2ux ", desc[i]);
172d40255d8SDavid du Colombier 		}
173d40255d8SDavid du Colombier 		fprint(2, "\n");
174d40255d8SDavid du Colombier 	}
175d40255d8SDavid du Colombier 	f->ptrvals = ptrrepvals;
176d40255d8SDavid du Colombier 	return nr;
177d40255d8SDavid du Colombier }
178d40255d8SDavid du Colombier 
179d5789509SDavid du Colombier /*
180d5789509SDavid du Colombier  * Try to recover from a babble error. A port reset is the only way out.
181d5789509SDavid du Colombier  * BUG: we should be careful not to reset a bundle with several devices.
182d5789509SDavid du Colombier  */
183d5789509SDavid du Colombier static void
recoverkb(KDev * f)184d5789509SDavid du Colombier recoverkb(KDev *f)
185d5789509SDavid du Colombier {
186d5789509SDavid du Colombier 	int i;
187d5789509SDavid du Colombier 
188d5789509SDavid du Colombier 	close(f->dev->dfd);		/* it's for usbd now */
189d5789509SDavid du Colombier 	devctl(f->dev, "reset");
190d5789509SDavid du Colombier 	for(i = 0; i < 10; i++){
191d40255d8SDavid du Colombier 		if(i == 5)
192d40255d8SDavid du Colombier 			f->bootp++;
193d5789509SDavid du Colombier 		sleep(500);
194d5789509SDavid du Colombier 		if(opendevdata(f->dev, ORDWR) >= 0){
195d40255d8SDavid du Colombier 			if(f->bootp)
196d40255d8SDavid du Colombier 				/* TODO func pointer */
197d40255d8SDavid du Colombier 				setbootproto(f, f->ep->id, nil, 0);
198d40255d8SDavid du Colombier 			else
199d40255d8SDavid du Colombier 				setfirstconfig(f, f->ep->id, nil, 0);
200d5789509SDavid du Colombier 			break;
201d5789509SDavid du Colombier 		}
202d5789509SDavid du Colombier 		/* else usbd still working... */
203d5789509SDavid du Colombier 	}
204d5789509SDavid du Colombier }
205d5789509SDavid du Colombier 
206906943f9SDavid du Colombier static void
kbfatal(KDev * kd,char * sts)207906943f9SDavid du Colombier kbfatal(KDev *kd, char *sts)
20883030dd5SDavid du Colombier {
209906943f9SDavid du Colombier 	Dev *dev;
21083030dd5SDavid du Colombier 
211906943f9SDavid du Colombier 	if(sts != nil)
21239dc1420SDavid du Colombier 		fprint(2, "kb: fatal: %s\n", sts);
21339dc1420SDavid du Colombier 	else
21439dc1420SDavid du Colombier 		fprint(2, "kb: exiting\n");
215906943f9SDavid du Colombier 	if(kd->repeatc != nil)
21639dc1420SDavid du Colombier 		nbsendul(kd->repeatc, Diemsg);
21739dc1420SDavid du Colombier 	dev = kd->dev;
21839dc1420SDavid du Colombier 	kd->dev = nil;
21939dc1420SDavid du Colombier 	if(kd->ep != nil)
220906943f9SDavid du Colombier 		closedev(kd->ep);
221906943f9SDavid du Colombier 	kd->ep = nil;
22239dc1420SDavid du Colombier 	devctl(dev, "detach");
223906943f9SDavid du Colombier 	closedev(dev);
224906943f9SDavid du Colombier 	/*
225906943f9SDavid du Colombier 	 * free(kd); done by closedev.
226906943f9SDavid du Colombier 	 */
227906943f9SDavid du Colombier 	threadexits(sts);
22883030dd5SDavid du Colombier }
22983030dd5SDavid du Colombier 
23083030dd5SDavid du Colombier static int
scale(KDev * f,int x)231906943f9SDavid du Colombier scale(KDev *f, int x)
23283030dd5SDavid du Colombier {
23383030dd5SDavid du Colombier 	int sign = 1;
23483030dd5SDavid du Colombier 
23583030dd5SDavid du Colombier 	if(x < 0){
23683030dd5SDavid du Colombier 		sign = -1;
23783030dd5SDavid du Colombier 		x = -x;
23883030dd5SDavid du Colombier 	}
23983030dd5SDavid du Colombier 	switch(x){
24083030dd5SDavid du Colombier 	case 0:
24183030dd5SDavid du Colombier 	case 1:
24283030dd5SDavid du Colombier 	case 2:
24383030dd5SDavid du Colombier 	case 3:
24483030dd5SDavid du Colombier 		break;
24583030dd5SDavid du Colombier 	case 4:
246906943f9SDavid du Colombier 		x = 6 + (f->accel>>2);
24783030dd5SDavid du Colombier 		break;
24883030dd5SDavid du Colombier 	case 5:
249906943f9SDavid du Colombier 		x = 9 + (f->accel>>1);
25083030dd5SDavid du Colombier 		break;
25183030dd5SDavid du Colombier 	default:
25283030dd5SDavid du Colombier 		x *= MaxAcc;
25383030dd5SDavid du Colombier 		break;
25483030dd5SDavid du Colombier 	}
25583030dd5SDavid du Colombier 	return sign*x;
25683030dd5SDavid du Colombier }
25783030dd5SDavid du Colombier 
258906943f9SDavid du Colombier /*
259906943f9SDavid du Colombier  * ps2 mouse is processed mostly at interrupt time.
260906943f9SDavid du Colombier  * for usb we do what we can.
261906943f9SDavid du Colombier  */
262906943f9SDavid du Colombier static void
sethipri(void)263906943f9SDavid du Colombier sethipri(void)
264906943f9SDavid du Colombier {
265906943f9SDavid du Colombier 	char fn[30];
266906943f9SDavid du Colombier 	int fd;
267906943f9SDavid du Colombier 
268d40255d8SDavid du Colombier 	snprint(fn, sizeof fn, "/proc/%d/ctl", getpid());
269906943f9SDavid du Colombier 	fd = open(fn, OWRITE);
270d40255d8SDavid du Colombier 	if(fd >= 0) {
271906943f9SDavid du Colombier 		fprint(fd, "pri 13");
272906943f9SDavid du Colombier 		close(fd);
273906943f9SDavid du Colombier 	}
274d40255d8SDavid du Colombier }
275d40255d8SDavid du Colombier 
276d40255d8SDavid du Colombier static int
ptrrepvals(KDev * kd,Chain * ch,int * px,int * py,int * pb)277d40255d8SDavid du Colombier ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb)
278d40255d8SDavid du Colombier {
279d40255d8SDavid du Colombier 	int i, x, y, b, c;
280d40255d8SDavid du Colombier 	static char buts[] = {0x0, 0x2, 0x1};
281d40255d8SDavid du Colombier 
282d40255d8SDavid du Colombier 	c = ch->e / 8;
283f7db6155SDavid du Colombier 
284f7db6155SDavid du Colombier 	/* sometimes there is a report id, sometimes not */
285f7db6155SDavid du Colombier 	if(c == kd->templ.sz + 1)
286f7db6155SDavid du Colombier 		if(ch->buf[0] == kd->templ.id)
287f7db6155SDavid du Colombier 			ch->b += 8;
288f7db6155SDavid du Colombier 		else
289f7db6155SDavid du Colombier 			return -1;
290d40255d8SDavid du Colombier 	parsereport(&kd->templ, ch);
291d40255d8SDavid du Colombier 
2929b7bf7dfSDavid du Colombier 	if(kd->debug > 1)
293d40255d8SDavid du Colombier 		dumpreport(&kd->templ);
294d40255d8SDavid du Colombier 	if(c < 3)
295d40255d8SDavid du Colombier 		return -1;
296d40255d8SDavid du Colombier 	x = hidifcval(&kd->templ, KindX, 0);
297d40255d8SDavid du Colombier 	y = hidifcval(&kd->templ, KindY, 0);
298d40255d8SDavid du Colombier 	b = 0;
299d40255d8SDavid du Colombier 	for(i = 0; i<sizeof buts; i++)
300d40255d8SDavid du Colombier 		b |= (hidifcval(&kd->templ, KindButtons, i) & 1) << buts[i];
301d40255d8SDavid du Colombier 	if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) > 0)	/* up */
302d40255d8SDavid du Colombier 		b |= 0x08;
3036f8e93f6SDavid du Colombier 	if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) < 0)	/* down */
3046f8e93f6SDavid du Colombier 		b |= 0x10;
305d40255d8SDavid du Colombier 
306d40255d8SDavid du Colombier 	*px = x;
307d40255d8SDavid du Colombier 	*py = y;
308d40255d8SDavid du Colombier 	*pb = b;
309d40255d8SDavid du Colombier 	return 0;
310d40255d8SDavid du Colombier }
311d40255d8SDavid du Colombier 
312d40255d8SDavid du Colombier static int
ptrbootpvals(KDev * kd,Chain * ch,int * px,int * py,int * pb)313d40255d8SDavid du Colombier ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb)
314d40255d8SDavid du Colombier {
315d40255d8SDavid du Colombier 	int b, c;
316d40255d8SDavid du Colombier 	char x, y;
317d40255d8SDavid du Colombier 	static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
318d40255d8SDavid du Colombier 
319d40255d8SDavid du Colombier 	c = ch->e / 8;
320d40255d8SDavid du Colombier 	if(c < 3)
321d40255d8SDavid du Colombier 		return -1;
322f1a26d48SDavid du Colombier 	if(kd->templ.nifcs){
323d40255d8SDavid du Colombier 		x = hidifcval(&kd->templ, KindX, 0);
324d40255d8SDavid du Colombier 		y = hidifcval(&kd->templ, KindY, 0);
325f1a26d48SDavid du Colombier 	}else{
326f1a26d48SDavid du Colombier 		/* no report descriptor for boot protocol */
327f1a26d48SDavid du Colombier 		x = ((signed char*)ch->buf)[1];
328f1a26d48SDavid du Colombier 		y = ((signed char*)ch->buf)[2];
329f1a26d48SDavid du Colombier 	}
330d40255d8SDavid du Colombier 
331d40255d8SDavid du Colombier 	b = maptab[ch->buf[0] & 0x7];
332d40255d8SDavid du Colombier 	if(c > 3 && ch->buf[3] == 1)		/* up */
333d40255d8SDavid du Colombier 		b |= 0x08;
334d40255d8SDavid du Colombier 	if(c > 3 && ch->buf[3] == 0xff)		/* down */
335d40255d8SDavid du Colombier 		b |= 0x10;
336d40255d8SDavid du Colombier 	*px = x;
337d40255d8SDavid du Colombier 	*py = y;
338d40255d8SDavid du Colombier 	*pb = b;
339d40255d8SDavid du Colombier 	return 0;
340d40255d8SDavid du Colombier }
341906943f9SDavid du Colombier 
342906943f9SDavid du Colombier static void
ptrwork(void * a)34383030dd5SDavid du Colombier ptrwork(void* a)
34483030dd5SDavid du Colombier {
345d40255d8SDavid du Colombier 	int hipri, mfd, nerrs, x, y, b, c, ptrfd;
346906943f9SDavid du Colombier 	char mbuf[80];
347d40255d8SDavid du Colombier 	Chain ch;
348906943f9SDavid du Colombier 	KDev* f = a;
34983030dd5SDavid du Colombier 
350f1a26d48SDavid du Colombier 	threadsetname("ptr %s", f->ep->dir);
351d5789509SDavid du Colombier 	hipri = nerrs = 0;
352906943f9SDavid du Colombier 	ptrfd = f->ep->dfd;
353906943f9SDavid du Colombier 	mfd = f->in->fd;
354d40255d8SDavid du Colombier 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > MaxChLen)
35539dc1420SDavid du Colombier 		kbfatal(f, "weird mouse maxpkt");
35683030dd5SDavid du Colombier 	for(;;){
357d40255d8SDavid du Colombier 		memset(ch.buf, 0, MaxChLen);
35839dc1420SDavid du Colombier 		if(f->ep == nil)
35939dc1420SDavid du Colombier 			kbfatal(f, nil);
360d40255d8SDavid du Colombier 		c = read(ptrfd, ch.buf, f->ep->maxpkt);
36139dc1420SDavid du Colombier 		assert(f->dev != nil);
36239dc1420SDavid du Colombier 		assert(f->ep != nil);
363d5789509SDavid du Colombier 		if(c < 0){
36439dc1420SDavid du Colombier 			dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
365d5789509SDavid du Colombier 			if(++nerrs < 3){
366d5789509SDavid du Colombier 				recoverkb(f);
367d5789509SDavid du Colombier 				continue;
368d5789509SDavid du Colombier 			}
369d5789509SDavid du Colombier 		}
370906943f9SDavid du Colombier 		if(c <= 0)
371906943f9SDavid du Colombier 			kbfatal(f, nil);
372d40255d8SDavid du Colombier 		ch.b = 0;
373d40255d8SDavid du Colombier 		ch.e = 8 * c;
374d40255d8SDavid du Colombier 		if(f->ptrvals(f, &ch, &x, &y, &b) < 0)
37583030dd5SDavid du Colombier 			continue;
376906943f9SDavid du Colombier 		if(f->accel){
377d40255d8SDavid du Colombier 			x = scale(f, x);
378d40255d8SDavid du Colombier 			y = scale(f, y);
37983030dd5SDavid du Colombier 		}
3809b7bf7dfSDavid du Colombier 		if(f->debug > 1)
38139dc1420SDavid du Colombier 			fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
382906943f9SDavid du Colombier 		seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
38339dc1420SDavid du Colombier 		if(write(mfd, mbuf, strlen(mbuf)) < 0)
38439dc1420SDavid du Colombier 			kbfatal(f, "mousein i/o");
385906943f9SDavid du Colombier 		if(hipri == 0){
386906943f9SDavid du Colombier 			sethipri();
387906943f9SDavid du Colombier 			hipri = 1;
38883030dd5SDavid du Colombier 		}
38983030dd5SDavid du Colombier 	}
39083030dd5SDavid du Colombier }
39183030dd5SDavid du Colombier 
39283030dd5SDavid du Colombier static void
stoprepeat(KDev * f)393906943f9SDavid du Colombier stoprepeat(KDev *f)
39483030dd5SDavid du Colombier {
395906943f9SDavid du Colombier 	sendul(f->repeatc, Awakemsg);
39683030dd5SDavid du Colombier }
39783030dd5SDavid du Colombier 
39883030dd5SDavid du Colombier static void
startrepeat(KDev * f,uchar esc1,uchar sc)399906943f9SDavid du Colombier startrepeat(KDev *f, uchar esc1, uchar sc)
40083030dd5SDavid du Colombier {
40183030dd5SDavid du Colombier 	ulong c;
40283030dd5SDavid du Colombier 
40383030dd5SDavid du Colombier 	if(esc1)
40483030dd5SDavid du Colombier 		c = SCesc1 << 8 | (sc & 0xff);
40583030dd5SDavid du Colombier 	else
40683030dd5SDavid du Colombier 		c = sc;
407906943f9SDavid du Colombier 	sendul(f->repeatc, c);
40883030dd5SDavid du Colombier }
40983030dd5SDavid du Colombier 
41083030dd5SDavid du Colombier static void
putscan(KDev * f,uchar esc,uchar sc)4119b7bf7dfSDavid du Colombier putscan(KDev *f, uchar esc, uchar sc)
41283030dd5SDavid du Colombier {
4139b7bf7dfSDavid du Colombier 	int kbinfd;
414906943f9SDavid du Colombier 	uchar s[2] = {SCesc1, 0};
41583030dd5SDavid du Colombier 
4169b7bf7dfSDavid du Colombier 	kbinfd = f->in->fd;
41783030dd5SDavid du Colombier 	if(sc == 0x41){
4189b7bf7dfSDavid du Colombier 		f->debug += 2;
41983030dd5SDavid du Colombier 		return;
42083030dd5SDavid du Colombier 	}
42183030dd5SDavid du Colombier 	if(sc == 0x42){
4229b7bf7dfSDavid du Colombier 		f->debug = 0;
42383030dd5SDavid du Colombier 		return;
42483030dd5SDavid du Colombier 	}
4259b7bf7dfSDavid du Colombier 	if(f->debug > 1)
42683030dd5SDavid du Colombier 		fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
42783030dd5SDavid du Colombier 	s[1] = sc;
42883030dd5SDavid du Colombier 	if(esc && sc != 0)
42983030dd5SDavid du Colombier 		write(kbinfd, s, 2);
43083030dd5SDavid du Colombier 	else if(sc != 0)
43183030dd5SDavid du Colombier 		write(kbinfd, s+1, 1);
43283030dd5SDavid du Colombier }
43383030dd5SDavid du Colombier 
43483030dd5SDavid du Colombier static void
repeatproc(void * a)435906943f9SDavid du Colombier repeatproc(void* a)
43683030dd5SDavid du Colombier {
437906943f9SDavid du Colombier 	KDev *f;
438906943f9SDavid du Colombier 	Channel *repeatc;
439906943f9SDavid du Colombier 	ulong l, t, i;
44083030dd5SDavid du Colombier 	uchar esc1, sc;
44183030dd5SDavid du Colombier 
4420cc6832dSDavid du Colombier 	threadsetname("kbd repeat");
443906943f9SDavid du Colombier 	/*
444906943f9SDavid du Colombier 	 * too many jumps here.
445906943f9SDavid du Colombier 	 * Rewrite instead of debug, if needed.
446906943f9SDavid du Colombier 	 */
447906943f9SDavid du Colombier 	f = a;
448906943f9SDavid du Colombier 	repeatc = f->repeatc;
449906943f9SDavid du Colombier 	l = Awakemsg;
450906943f9SDavid du Colombier Repeat:
451906943f9SDavid du Colombier 	if(l == Diemsg)
452906943f9SDavid du Colombier 		goto Abort;
453906943f9SDavid du Colombier 	while(l == Awakemsg)
454906943f9SDavid du Colombier 		l = recvul(repeatc);
455906943f9SDavid du Colombier 	if(l == Diemsg)
456906943f9SDavid du Colombier 		goto Abort;
45783030dd5SDavid du Colombier 	esc1 = l >> 8;
45883030dd5SDavid du Colombier 	sc = l;
459906943f9SDavid du Colombier 	t = 160;
46083030dd5SDavid du Colombier 	for(;;){
461906943f9SDavid du Colombier 		for(i = 0; i < t; i += 5){
462906943f9SDavid du Colombier 			if(l = nbrecvul(repeatc))
463906943f9SDavid du Colombier 				goto Repeat;
464906943f9SDavid du Colombier 			sleep(5);
46583030dd5SDavid du Colombier 		}
4669b7bf7dfSDavid du Colombier 		putscan(f, esc1, sc);
467906943f9SDavid du Colombier 		t = 30;
46883030dd5SDavid du Colombier 	}
469906943f9SDavid du Colombier Abort:
470906943f9SDavid du Colombier 	chanfree(repeatc);
471906943f9SDavid du Colombier 	threadexits("aborted");
472906943f9SDavid du Colombier 
47383030dd5SDavid du Colombier }
47483030dd5SDavid du Colombier 
47583030dd5SDavid du Colombier 
476*58da3067SDavid du Colombier #define hasesc1(sc)	((sc) >= 0x47 || (sc) == 0x38)
47783030dd5SDavid du Colombier 
47883030dd5SDavid du Colombier static void
putmod(KDev * f,uchar mods,uchar omods,uchar mask,uchar esc,uchar sc)4799b7bf7dfSDavid du Colombier putmod(KDev *f, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
48083030dd5SDavid du Colombier {
481906943f9SDavid du Colombier 	/* BUG: Should be a single write */
48283030dd5SDavid du Colombier 	if((mods&mask) && !(omods&mask))
4839b7bf7dfSDavid du Colombier 		putscan(f, esc, sc);
48483030dd5SDavid du Colombier 	if(!(mods&mask) && (omods&mask))
4859b7bf7dfSDavid du Colombier 		putscan(f, esc, Keyup|sc);
48683030dd5SDavid du Colombier }
48783030dd5SDavid du Colombier 
48883030dd5SDavid du Colombier /*
48983030dd5SDavid du Colombier  * This routine diffs the state with the last known state
49083030dd5SDavid du Colombier  * and invents the scan codes that would have been sent
49183030dd5SDavid du Colombier  * by a non-usb keyboard in that case. This also requires supplying
49283030dd5SDavid du Colombier  * the extra esc1 byte as well as keyup flags.
49383030dd5SDavid du Colombier  * The aim is to allow future addition of other keycode pages
49483030dd5SDavid du Colombier  * for other keyboards.
49583030dd5SDavid du Colombier  */
496906943f9SDavid du Colombier static uchar
putkeys(KDev * f,uchar buf[],uchar obuf[],int n,uchar dk)497906943f9SDavid du Colombier putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk)
49883030dd5SDavid du Colombier {
49983030dd5SDavid du Colombier 	int i, j;
500906943f9SDavid du Colombier 	uchar uk;
50183030dd5SDavid du Colombier 
5029b7bf7dfSDavid du Colombier 	putmod(f, buf[0], obuf[0], Mctrl, 0, SCctrl);
5039b7bf7dfSDavid du Colombier 	putmod(f, buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
5049b7bf7dfSDavid du Colombier 	putmod(f, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
5059b7bf7dfSDavid du Colombier 	putmod(f, buf[0], obuf[0], Mcompose, 0, SCcompose);
5069b7bf7dfSDavid du Colombier 	putmod(f, buf[0], obuf[0], Maltgr, 1, SCcompose);
50783030dd5SDavid du Colombier 
50883030dd5SDavid du Colombier 	/* Report key downs */
50983030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
51083030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
51183030dd5SDavid du Colombier 			if(buf[i] == obuf[j])
51283030dd5SDavid du Colombier 			 	break;
51383030dd5SDavid du Colombier 		if(j == n && buf[i] != 0){
514906943f9SDavid du Colombier 			dk = sctab[buf[i]];
5159b7bf7dfSDavid du Colombier 			putscan(f, hasesc1(dk), dk);
516906943f9SDavid du Colombier 			startrepeat(f, hasesc1(dk), dk);
51783030dd5SDavid du Colombier 		}
51883030dd5SDavid du Colombier 	}
51983030dd5SDavid du Colombier 
52083030dd5SDavid du Colombier 	/* Report key ups */
521906943f9SDavid du Colombier 	uk = 0;
52283030dd5SDavid du Colombier 	for(i = 2; i < n; i++){
52383030dd5SDavid du Colombier 		for(j = 2; j < n; j++)
52483030dd5SDavid du Colombier 			if(obuf[i] == buf[j])
52583030dd5SDavid du Colombier 				break;
52683030dd5SDavid du Colombier 		if(j == n && obuf[i] != 0){
527906943f9SDavid du Colombier 			uk = sctab[obuf[i]];
5289b7bf7dfSDavid du Colombier 			putscan(f, hasesc1(uk), uk|Keyup);
52983030dd5SDavid du Colombier 		}
53083030dd5SDavid du Colombier 	}
531906943f9SDavid du Colombier 	if(uk && (dk == 0 || dk == uk)){
532906943f9SDavid du Colombier 		stoprepeat(f);
533906943f9SDavid du Colombier 		dk = 0;
534906943f9SDavid du Colombier 	}
535906943f9SDavid du Colombier 	return dk;
53683030dd5SDavid du Colombier }
53783030dd5SDavid du Colombier 
53883030dd5SDavid du Colombier static int
kbdbusy(uchar * buf,int n)53983030dd5SDavid du Colombier kbdbusy(uchar* buf, int n)
54083030dd5SDavid du Colombier {
54183030dd5SDavid du Colombier 	int i;
54283030dd5SDavid du Colombier 
54383030dd5SDavid du Colombier 	for(i = 1; i < n; i++)
54483030dd5SDavid du Colombier 		if(buf[i] == 0 || buf[i] != buf[0])
54583030dd5SDavid du Colombier 			return 0;
54683030dd5SDavid du Colombier 	return 1;
54783030dd5SDavid du Colombier }
54883030dd5SDavid du Colombier 
549906943f9SDavid du Colombier static void
kbdwork(void * a)55083030dd5SDavid du Colombier kbdwork(void *a)
55183030dd5SDavid du Colombier {
552a23bc242SDavid du Colombier 	int c, i, kbdfd, nerrs;
553a23bc242SDavid du Colombier 	uchar dk, buf[64], lbuf[64];
554a23bc242SDavid du Colombier 	char err[128];
555906943f9SDavid du Colombier 	KDev *f = a;
55683030dd5SDavid du Colombier 
557f1a26d48SDavid du Colombier 	threadsetname("kbd %s", f->ep->dir);
558906943f9SDavid du Colombier 	kbdfd = f->ep->dfd;
559906943f9SDavid du Colombier 
560906943f9SDavid du Colombier 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
561906943f9SDavid du Colombier 		kbfatal(f, "weird maxpkt");
562906943f9SDavid du Colombier 
563906943f9SDavid du Colombier 	f->repeatc = chancreate(sizeof(ulong), 0);
564906943f9SDavid du Colombier 	if(f->repeatc == nil)
565906943f9SDavid du Colombier 		kbfatal(f, "chancreate failed");
566906943f9SDavid du Colombier 
567906943f9SDavid du Colombier 	proccreate(repeatproc, f, Stack);
56883030dd5SDavid du Colombier 	memset(lbuf, 0, sizeof lbuf);
569a23bc242SDavid du Colombier 	dk = nerrs = 0;
57083030dd5SDavid du Colombier 	for(;;){
57183030dd5SDavid du Colombier 		memset(buf, 0, sizeof buf);
572906943f9SDavid du Colombier 		c = read(kbdfd, buf, f->ep->maxpkt);
57339dc1420SDavid du Colombier 		assert(f->dev != nil);
57439dc1420SDavid du Colombier 		assert(f->ep != nil);
575a23bc242SDavid du Colombier 		if(c < 0){
576a23bc242SDavid du Colombier 			rerrstr(err, sizeof(err));
577d5789509SDavid du Colombier 			fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
578a23bc242SDavid du Colombier 			if(strstr(err, "babble") != 0 && ++nerrs < 3){
579a23bc242SDavid du Colombier 				recoverkb(f);
580a23bc242SDavid du Colombier 				continue;
581a23bc242SDavid du Colombier 			}
582a23bc242SDavid du Colombier 		}
583906943f9SDavid du Colombier 		if(c <= 0)
58439dc1420SDavid du Colombier 			kbfatal(f, nil);
58583030dd5SDavid du Colombier 		if(c < 3)
58683030dd5SDavid du Colombier 			continue;
58783030dd5SDavid du Colombier 		if(kbdbusy(buf + 2, c - 2))
58883030dd5SDavid du Colombier 			continue;
5899b7bf7dfSDavid du Colombier 		if(usbdebug > 2 || f->debug > 1){
59083030dd5SDavid du Colombier 			fprint(2, "kbd mod %x: ", buf[0]);
59183030dd5SDavid du Colombier 			for(i = 2; i < c; i++)
59283030dd5SDavid du Colombier 				fprint(2, "kc %x ", buf[i]);
59383030dd5SDavid du Colombier 			fprint(2, "\n");
59483030dd5SDavid du Colombier 		}
595906943f9SDavid du Colombier 		dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
59683030dd5SDavid du Colombier 		memmove(lbuf, buf, c);
597a23bc242SDavid du Colombier 		nerrs = 0;
59883030dd5SDavid du Colombier 	}
59983030dd5SDavid du Colombier }
60083030dd5SDavid du Colombier 
60183030dd5SDavid du Colombier static void
freekdev(void * a)602906943f9SDavid du Colombier freekdev(void *a)
60383030dd5SDavid du Colombier {
604906943f9SDavid du Colombier 	KDev *kd;
60583030dd5SDavid du Colombier 
606906943f9SDavid du Colombier 	kd = a;
607906943f9SDavid du Colombier 	if(kd->in != nil){
608906943f9SDavid du Colombier 		qlock(&inlck);
609906943f9SDavid du Colombier 		if(--kd->in->ref == 0){
610906943f9SDavid du Colombier 			close(kd->in->fd);
611906943f9SDavid du Colombier 			kd->in->fd = -1;
61283030dd5SDavid du Colombier 		}
613906943f9SDavid du Colombier 		qunlock(&inlck);
61483030dd5SDavid du Colombier 	}
61539dc1420SDavid du Colombier 	dprint(2, "freekdev\n");
616906943f9SDavid du Colombier 	free(kd);
61783030dd5SDavid du Colombier }
61883030dd5SDavid du Colombier 
61983030dd5SDavid du Colombier static void
kbstart(Dev * d,Ep * ep,Kin * in,void (* f)(void *),KDev * kd)620d40255d8SDavid du Colombier kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd)
621906943f9SDavid du Colombier {
62246d884bbSDavid du Colombier 	uchar desc[512];
6230cc6832dSDavid du Colombier 	int n, res;
624906943f9SDavid du Colombier 
625906943f9SDavid du Colombier 	qlock(&inlck);
626906943f9SDavid du Colombier 	if(in->fd < 0){
627906943f9SDavid du Colombier 		in->fd = open(in->name, OWRITE);
628906943f9SDavid du Colombier 		if(in->fd < 0){
629906943f9SDavid du Colombier 			fprint(2, "kb: %s: %r\n", in->name);
630906943f9SDavid du Colombier 			qunlock(&inlck);
631906943f9SDavid du Colombier 			return;
632906943f9SDavid du Colombier 		}
633906943f9SDavid du Colombier 	}
63439dc1420SDavid du Colombier 	in->ref++;	/* for kd->in = in */
635906943f9SDavid du Colombier 	qunlock(&inlck);
636906943f9SDavid du Colombier 	d->free = freekdev;
637906943f9SDavid du Colombier 	kd->in = in;
638906943f9SDavid du Colombier 	kd->dev = d;
639d40255d8SDavid du Colombier 	res = -1;
640f7db6155SDavid du Colombier 	kd->ep = openep(d, ep->id);
641f7db6155SDavid du Colombier 	if(kd->ep == nil){
642f7db6155SDavid du Colombier 		fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
643f7db6155SDavid du Colombier 		return;
644f7db6155SDavid du Colombier 	}
6450cc6832dSDavid du Colombier 	if(in == &kbdin){
6460cc6832dSDavid du Colombier 		/*
6470cc6832dSDavid du Colombier 		 * DWC OTG controller misses some split transaction inputs.
6480cc6832dSDavid du Colombier 		 * Set nonzero idle time to return more frequent reports
6490cc6832dSDavid du Colombier 		 * of keyboard state, to avoid losing key up/down events.
6500cc6832dSDavid du Colombier 		 */
6510cc6832dSDavid du Colombier 		n = read(d->cfd, desc, sizeof desc - 1);
6520cc6832dSDavid du Colombier 		if(n > 0){
6530cc6832dSDavid du Colombier 			desc[n] = 0;
6540cc6832dSDavid du Colombier 			if(strstr((char*)desc, "dwcotg") != nil)
6550cc6832dSDavid du Colombier 				kd->idle = Dwcidle;
6560cc6832dSDavid du Colombier 		}
6570cc6832dSDavid du Colombier 	}
658d40255d8SDavid du Colombier 	if(!kd->bootp)
659d40255d8SDavid du Colombier 		res= setfirstconfig(kd, ep->id, desc, sizeof desc);
660d40255d8SDavid du Colombier 	if(res > 0)
661d40255d8SDavid du Colombier 		res = parsereportdesc(&kd->templ, desc, sizeof desc);
662d40255d8SDavid du Colombier 	/* if we could not set the first config, we give up */
663d40255d8SDavid du Colombier 	if(kd->bootp || res < 0){
664d40255d8SDavid du Colombier 		kd->bootp = 1;
665d40255d8SDavid du Colombier 		if(setbootproto(kd, ep->id, nil, 0) < 0){
666906943f9SDavid du Colombier 			fprint(2, "kb: %s: bootproto: %r\n", d->dir);
667906943f9SDavid du Colombier 			return;
668906943f9SDavid du Colombier 		}
6699b7bf7dfSDavid du Colombier 	}else if(kd->debug)
670d40255d8SDavid du Colombier 		dumpreport(&kd->templ);
671906943f9SDavid du Colombier 	if(opendevdata(kd->ep, OREAD) < 0){
672906943f9SDavid du Colombier 		fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir);
673906943f9SDavid du Colombier 		closedev(kd->ep);
674906943f9SDavid du Colombier 		kd->ep = nil;
675906943f9SDavid du Colombier 		return;
676906943f9SDavid du Colombier 	}
677906943f9SDavid du Colombier 
678906943f9SDavid du Colombier 	incref(d);
679906943f9SDavid du Colombier 	proccreate(f, kd, Stack);
680906943f9SDavid du Colombier }
681906943f9SDavid du Colombier 
682906943f9SDavid du Colombier static int
usage(void)68383030dd5SDavid du Colombier usage(void)
68483030dd5SDavid du Colombier {
685d40255d8SDavid du Colombier 	werrstr("usage: usb/kb [-bdkm] [-a n] [-N nb]");
686906943f9SDavid du Colombier 	return -1;
68783030dd5SDavid du Colombier }
68883030dd5SDavid du Colombier 
689906943f9SDavid du Colombier int
kbmain(Dev * d,int argc,char * argv[])690906943f9SDavid du Colombier kbmain(Dev *d, int argc, char* argv[])
69183030dd5SDavid du Colombier {
6929b7bf7dfSDavid du Colombier 	int bootp, i, kena, pena, accel, devid, debug;
693906943f9SDavid du Colombier 	Ep *ep;
694d40255d8SDavid du Colombier 	KDev *kd;
695d40255d8SDavid du Colombier 	Usbdev *ud;
69683030dd5SDavid du Colombier 
697906943f9SDavid du Colombier 	kena = pena = 1;
698d40255d8SDavid du Colombier 	bootp = 0;
699906943f9SDavid du Colombier 	accel = 0;
7009b7bf7dfSDavid du Colombier 	debug = 0;
701ed868a7cSDavid du Colombier 	devid = d->id;
70283030dd5SDavid du Colombier 	ARGBEGIN{
70383030dd5SDavid du Colombier 	case 'a':
704ed868a7cSDavid du Colombier 		accel = strtol(EARGF(usage()), nil, 0);
70583030dd5SDavid du Colombier 		break;
70683030dd5SDavid du Colombier 	case 'd':
7079b7bf7dfSDavid du Colombier 		debug++;
70883030dd5SDavid du Colombier 		break;
70983030dd5SDavid du Colombier 	case 'k':
710906943f9SDavid du Colombier 		kena = 1;
711906943f9SDavid du Colombier 		pena = 0;
71283030dd5SDavid du Colombier 		break;
71383030dd5SDavid du Colombier 	case 'm':
714906943f9SDavid du Colombier 		kena = 0;
715906943f9SDavid du Colombier 		pena = 1;
71683030dd5SDavid du Colombier 		break;
717ed868a7cSDavid du Colombier 	case 'N':
718ed868a7cSDavid du Colombier 		devid = atoi(EARGF(usage()));		/* ignore dev number */
719ed868a7cSDavid du Colombier 		break;
720d40255d8SDavid du Colombier 	case 'b':
721d40255d8SDavid du Colombier 		bootp++;
722d40255d8SDavid du Colombier 		break;
72383030dd5SDavid du Colombier 	default:
724906943f9SDavid du Colombier 		return usage();
72583030dd5SDavid du Colombier 	}ARGEND;
726d40255d8SDavid du Colombier 	if(argc != 0)
727906943f9SDavid du Colombier 		return usage();
728ed868a7cSDavid du Colombier 	USED(devid);
729906943f9SDavid du Colombier 	ud = d->usb;
730906943f9SDavid du Colombier 	d->aux = nil;
731906943f9SDavid du Colombier 	dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref);
732d40255d8SDavid du Colombier 
733d40255d8SDavid du Colombier 	if(kena)
734d40255d8SDavid du Colombier 		for(i = 0; i < nelem(ud->ep); i++)
735d40255d8SDavid du Colombier 			if((ep = ud->ep[i]) == nil)
736d40255d8SDavid du Colombier 				break;
737d40255d8SDavid du Colombier 			else if(ep->iface->csp == KbdCSP)
738d40255d8SDavid du Colombier 				bootp = 1;
739d40255d8SDavid du Colombier 
740906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->ep); i++){
741906943f9SDavid du Colombier 		if((ep = ud->ep[i]) == nil)
74246d884bbSDavid du Colombier 			continue;
743d40255d8SDavid du Colombier 		if(kena && ep->type == Eintr && ep->dir == Ein &&
744d40255d8SDavid du Colombier 		    ep->iface->csp == KbdCSP){
745d40255d8SDavid du Colombier 			kd = d->aux = emallocz(sizeof(KDev), 1);
746d40255d8SDavid du Colombier 			kd->accel = 0;
747d40255d8SDavid du Colombier 			kd->bootp = 1;
7489b7bf7dfSDavid du Colombier 			kd->debug = debug;
749d40255d8SDavid du Colombier 			kbstart(d, ep, &kbdin, kbdwork, kd);
750d40255d8SDavid du Colombier 		}
751d40255d8SDavid du Colombier 		if(pena && ep->type == Eintr && ep->dir == Ein &&
752d40255d8SDavid du Colombier 		    ep->iface->csp == PtrCSP){
753d40255d8SDavid du Colombier 			kd = d->aux = emallocz(sizeof(KDev), 1);
754d40255d8SDavid du Colombier 			kd->accel = accel;
755d40255d8SDavid du Colombier 			kd->bootp = bootp;
7569b7bf7dfSDavid du Colombier 			kd->debug = debug;
757d40255d8SDavid du Colombier 			kbstart(d, ep, &ptrin, ptrwork, kd);
758d40255d8SDavid du Colombier 		}
759906943f9SDavid du Colombier 	}
760906943f9SDavid du Colombier 	return 0;
76183030dd5SDavid du Colombier }
762