xref: /plan9/sys/src/cmd/usb/kb/kb.c (revision 401314a3b4602c168a19b28ed47ba5cbefe42fe0)
1 /*
2  * USB Human Interaction Device: keyboard and mouse.
3  *
4  * If there's no usb keyboard, it tries to setup the mouse, if any.
5  * It should be started at boot time.
6  *
7  * Mouse events are converted to the format of mouse(3)'s mousein file.
8  * Keyboard keycodes are translated to scan codes and sent to kbin(3).
9  *
10  * If there is no keyboard, it tries to setup the mouse properly, else it falls
11  * back to boot protocol.
12  */
13 
14 #include <u.h>
15 #include <libc.h>
16 #include <thread.h>
17 #include "usb.h"
18 #include "hid.h"
19 
20 enum
21 {
22 	Awakemsg= 0xdeaddead,
23 	Diemsg	= 0xbeefbeef,
24 };
25 
26 typedef struct KDev KDev;
27 typedef struct Kin Kin;
28 
29 struct KDev
30 {
31 	Dev*	dev;		/* usb device*/
32 	Dev*	ep;		/* endpoint to get events */
33 	Kin*	in;		/* used to send events to kernel */
34 	Channel*repeatc;	/* only for keyboard */
35 	int	accel;		/* only for mouse */
36 	int	bootp;		/* has associated keyboard */
37 	HidRepTempl templ;
38 	int	(*ptrvals)(KDev *kd, Chain *ch, int *px, int *py, int *pb);
39 };
40 
41 /*
42  * Kbdin and mousein files must be shared among all instances.
43  */
44 struct Kin
45 {
46 	int	ref;
47 	int	fd;
48 	char*	name;
49 };
50 
51 /*
52  * Map for the logitech bluetooth mouse with 8 buttons and wheels.
53  *	{ ptr ->mouse}
54  *	{ 0x01, 0x01 },	// left
55  *	{ 0x04, 0x02 },	// middle
56  *	{ 0x02, 0x04 },	// right
57  *	{ 0x40, 0x08 },	// up
58  *	{ 0x80, 0x10 },	// down
59  *	{ 0x10, 0x08 },	// side up
60  *	{ 0x08, 0x10 },	// side down
61  *	{ 0x20, 0x02 }, // page
62  * besides wheel and regular up/down report the 4th byte as 1/-1
63  */
64 
65 /*
66  * key code to scan code; for the page table used by
67  * the logitech bluetooth keyboard.
68  */
69 static char sctab[256] =
70 {
71 [0x00]	0x0,	0x0,	0x0,	0x0,	0x1e,	0x30,	0x2e,	0x20,
72 [0x08]	0x12,	0x21,	0x22,	0x23,	0x17,	0x24,	0x25,	0x26,
73 [0x10]	0x32,	0x31,	0x18,	0x19,	0x10,	0x13,	0x1f,	0x14,
74 [0x18]	0x16,	0x2f,	0x11,	0x2d,	0x15,	0x2c,	0x2,	0x3,
75 [0x20]	0x4,	0x5,	0x6,	0x7,	0x8,	0x9,	0xa,	0xb,
76 [0x28]	0x1c,	0x1,	0xe,	0xf,	0x39,	0xc,	0xd,	0x1a,
77 [0x30]	0x1b,	0x2b,	0x2b,	0x27,	0x28,	0x29,	0x33,	0x34,
78 [0x38]	0x35,	0x3a,	0x3b,	0x3c,	0x3d,	0x3e,	0x3f,	0x40,
79 [0x40]	0x41,	0x42,	0x43,	0x44,	0x57,	0x58,	0x63,	0x46,
80 [0x48]	0x77,	0x52,	0x47,	0x49,	0x53,	0x4f,	0x51,	0x4d,
81 [0x50]	0x4b,	0x50,	0x48,	0x45,	0x35,	0x37,	0x4a,	0x4e,
82 [0x58]	0x1c,	0x4f,	0x50,	0x51,	0x4b,	0x4c,	0x4d,	0x47,
83 [0x60]	0x48,	0x49,	0x52,	0x53,	0x56,	0x7f,	0x74,	0x75,
84 [0x68]	0x55,	0x59,	0x5a,	0x5b,	0x5c,	0x5d,	0x5e,	0x5f,
85 [0x70]	0x78,	0x79,	0x7a,	0x7b,	0x0,	0x0,	0x0,	0x0,
86 [0x78]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x71,
87 [0x80]	0x73,	0x72,	0x0,	0x0,	0x0,	0x7c,	0x0,	0x0,
88 [0x88]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
89 [0x90]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
90 [0x98]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
91 [0xa0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
92 [0xa8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
93 [0xb0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
94 [0xb8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
95 [0xc0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
96 [0xc8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
97 [0xd0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
98 [0xd8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
99 [0xe0]	0x1d,	0x2a,	0x38,	0x7d,	0x61,	0x36,	0x64,	0x7e,
100 [0xe8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x73,	0x72,	0x71,
101 [0xf0]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
102 [0xf8]	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,	0x0,
103 };
104 
105 static QLock inlck;
106 static Kin kbdin =
107 {
108 	.ref = 0,
109 	.name = "#Ι/kbin",
110 	.fd = -1,
111 };
112 static Kin ptrin =
113 {
114 	.ref = 0,
115 	.name = "#m/mousein",
116 	.fd = -1,
117 };
118 
119 static int kbdebug;
120 
121 static int ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb);
122 static int ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb);
123 
124 static int
125 setbootproto(KDev* f, int eid, uchar *, int)
126 {
127 	int r, id;
128 
129 	f->ptrvals = ptrbootpvals;
130 	r = Rh2d|Rclass|Riface;
131 	dprint(2, "setting boot protocol\n");
132 	id = f->dev->usb->ep[eid]->iface->id;
133 	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
134 }
135 
136 static uchar ignoredesc[128];
137 
138 static int
139 setfirstconfig(KDev* f, int eid, uchar *desc, int descsz)
140 {
141 	int nr, r, id, i;
142 
143 	dprint(2, "setting first config\n");
144 	if(desc == nil){
145 		descsz = sizeof ignoredesc;
146 		desc = ignoredesc;
147 	}
148 	id = f->dev->usb->ep[eid]->iface->id;
149 	r = Rh2d | Rstd | Rdev;
150 	nr =usbcmd(f->dev,  r, Rsetconf, 1, id, nil, 0);
151 	if(nr < 0)
152 		return -1;
153 	r = Rh2d | Rclass | Riface;
154 	nr=usbcmd(f->dev,   r, Setidle,  0, id, nil, 0);
155 	if(nr < 0)
156 		return -1;
157 	r = Rd2h | Rstd | Riface;
158 	nr=usbcmd(f->dev,  r, Rgetdesc, Dreport<<8, id, desc, descsz);
159 	if(nr < 0)
160 		return -1;
161 	if(kbdebug && nr > 0){
162 		fprint(2, "report descriptor:");
163 		for(i = 0; i < nr; i++){
164 			if(i%8 == 0)
165 				fprint(2, "\n\t");
166 			fprint(2, "%#2.2ux ", desc[i]);
167 		}
168 		fprint(2, "\n");
169 	}
170 	f->ptrvals = ptrrepvals;
171 	return nr;
172 }
173 
174 /*
175  * Try to recover from a babble error. A port reset is the only way out.
176  * BUG: we should be careful not to reset a bundle with several devices.
177  */
178 static void
179 recoverkb(KDev *f)
180 {
181 	int i;
182 
183 	close(f->dev->dfd);		/* it's for usbd now */
184 	devctl(f->dev, "reset");
185 	for(i = 0; i < 10; i++){
186 		if(i == 5)
187 			f->bootp++;
188 		sleep(500);
189 		if(opendevdata(f->dev, ORDWR) >= 0){
190 			if(f->bootp)
191 				/* TODO func pointer */
192 				setbootproto(f, f->ep->id, nil, 0);
193 			else
194 				setfirstconfig(f, f->ep->id, nil, 0);
195 			break;
196 		}
197 		/* else usbd still working... */
198 	}
199 }
200 
201 static void
202 kbfatal(KDev *kd, char *sts)
203 {
204 	Dev *dev;
205 
206 	if(sts != nil)
207 		fprint(2, "kb: fatal: %s\n", sts);
208 	else
209 		fprint(2, "kb: exiting\n");
210 	if(kd->repeatc != nil)
211 		nbsendul(kd->repeatc, Diemsg);
212 	dev = kd->dev;
213 	kd->dev = nil;
214 	if(kd->ep != nil)
215 		closedev(kd->ep);
216 	kd->ep = nil;
217 	devctl(dev, "detach");
218 	closedev(dev);
219 	/*
220 	 * free(kd); done by closedev.
221 	 */
222 	threadexits(sts);
223 }
224 
225 static int
226 scale(KDev *f, int x)
227 {
228 	int sign = 1;
229 
230 	if(x < 0){
231 		sign = -1;
232 		x = -x;
233 	}
234 	switch(x){
235 	case 0:
236 	case 1:
237 	case 2:
238 	case 3:
239 		break;
240 	case 4:
241 		x = 6 + (f->accel>>2);
242 		break;
243 	case 5:
244 		x = 9 + (f->accel>>1);
245 		break;
246 	default:
247 		x *= MaxAcc;
248 		break;
249 	}
250 	return sign*x;
251 }
252 
253 /*
254  * ps2 mouse is processed mostly at interrupt time.
255  * for usb we do what we can.
256  */
257 static void
258 sethipri(void)
259 {
260 	char fn[30];
261 	int fd;
262 
263 	snprint(fn, sizeof fn, "/proc/%d/ctl", getpid());
264 	fd = open(fn, OWRITE);
265 	if(fd >= 0) {
266 		fprint(fd, "pri 13");
267 		close(fd);
268 	}
269 }
270 
271 static int
272 ptrrepvals(KDev *kd, Chain *ch, int *px, int *py, int *pb)
273 {
274 	int i, x, y, b, c;
275 	static char buts[] = {0x0, 0x2, 0x1};
276 
277 	c = ch->e / 8;
278 
279 	/* sometimes there is a report id, sometimes not */
280 	if(c == kd->templ.sz + 1)
281 		if(ch->buf[0] == kd->templ.id)
282 			ch->b += 8;
283 		else
284 			return -1;
285 	parsereport(&kd->templ, ch);
286 
287 	if(kbdebug)
288 		dumpreport(&kd->templ);
289 	if(c < 3)
290 		return -1;
291 	x = hidifcval(&kd->templ, KindX, 0);
292 	y = hidifcval(&kd->templ, KindY, 0);
293 	b = 0;
294 	for(i = 0; i<sizeof buts; i++)
295 		b |= (hidifcval(&kd->templ, KindButtons, i) & 1) << buts[i];
296 	if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) > 0)	/* up */
297 		b |= 0x10;
298 	if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) < 0)	/* down */
299 		b |= 0x08;
300 
301 	*px = x;
302 	*py = y;
303 	*pb = b;
304 	return 0;
305 }
306 
307 static int
308 ptrbootpvals(KDev *kd, Chain *ch, int *px, int *py, int *pb)
309 {
310 	int b, c;
311 	char x, y;
312 	static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7};
313 
314 	c = ch->e / 8;
315 	if(c < 3)
316 		return -1;
317 	x = hidifcval(&kd->templ, KindX, 0);
318 	y = hidifcval(&kd->templ, KindY, 0);
319 
320 	b = maptab[ch->buf[0] & 0x7];
321 	if(c > 3 && ch->buf[3] == 1)		/* up */
322 		b |= 0x08;
323 	if(c > 3 && ch->buf[3] == 0xff)		/* down */
324 		b |= 0x10;
325 	*px = x;
326 	*py = y;
327 	*pb = b;
328 	return 0;
329 }
330 
331 static void
332 ptrwork(void* a)
333 {
334 	int hipri, mfd, nerrs, x, y, b, c, ptrfd;
335 	char mbuf[80];
336 	Chain ch;
337 	KDev* f = a;
338 
339 	hipri = nerrs = 0;
340 	ptrfd = f->ep->dfd;
341 	mfd = f->in->fd;
342 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > MaxChLen)
343 		kbfatal(f, "weird mouse maxpkt");
344 	for(;;){
345 		memset(ch.buf, 0, MaxChLen);
346 		if(f->ep == nil)
347 			kbfatal(f, nil);
348 		c = read(ptrfd, ch.buf, f->ep->maxpkt);
349 		assert(f->dev != nil);
350 		assert(f->ep != nil);
351 		if(c < 0){
352 			dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir);
353 			if(++nerrs < 3){
354 				recoverkb(f);
355 				continue;
356 			}
357 		}
358 		if(c <= 0)
359 			kbfatal(f, nil);
360 		ch.b = 0;
361 		ch.e = 8 * c;
362 		if(f->ptrvals(f, &ch, &x, &y, &b) < 0)
363 			continue;
364 		if(f->accel){
365 			x = scale(f, x);
366 			y = scale(f, y);
367 		}
368 		if(kbdebug > 1)
369 			fprint(2, "kb: m%11d %11d %11d\n", x, y, b);
370 		seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b);
371 		if(write(mfd, mbuf, strlen(mbuf)) < 0)
372 			kbfatal(f, "mousein i/o");
373 		if(hipri == 0){
374 			sethipri();
375 			hipri = 1;
376 		}
377 	}
378 }
379 
380 static void
381 stoprepeat(KDev *f)
382 {
383 	sendul(f->repeatc, Awakemsg);
384 }
385 
386 static void
387 startrepeat(KDev *f, uchar esc1, uchar sc)
388 {
389 	ulong c;
390 
391 	if(esc1)
392 		c = SCesc1 << 8 | (sc & 0xff);
393 	else
394 		c = sc;
395 	sendul(f->repeatc, c);
396 }
397 
398 static void
399 putscan(int kbinfd, uchar esc, uchar sc)
400 {
401 	uchar s[2] = {SCesc1, 0};
402 
403 	if(sc == 0x41){
404 		kbdebug += 2;
405 		return;
406 	}
407 	if(sc == 0x42){
408 		kbdebug = 0;
409 		return;
410 	}
411 	if(kbdebug)
412 		fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc);
413 	s[1] = sc;
414 	if(esc && sc != 0)
415 		write(kbinfd, s, 2);
416 	else if(sc != 0)
417 		write(kbinfd, s+1, 1);
418 }
419 
420 static void
421 repeatproc(void* a)
422 {
423 	KDev *f;
424 	Channel *repeatc;
425 	int kbdinfd;
426 	ulong l, t, i;
427 	uchar esc1, sc;
428 
429 	/*
430 	 * too many jumps here.
431 	 * Rewrite instead of debug, if needed.
432 	 */
433 	f = a;
434 	repeatc = f->repeatc;
435 	kbdinfd = f->in->fd;
436 	l = Awakemsg;
437 Repeat:
438 	if(l == Diemsg)
439 		goto Abort;
440 	while(l == Awakemsg)
441 		l = recvul(repeatc);
442 	if(l == Diemsg)
443 		goto Abort;
444 	esc1 = l >> 8;
445 	sc = l;
446 	t = 160;
447 	for(;;){
448 		for(i = 0; i < t; i += 5){
449 			if(l = nbrecvul(repeatc))
450 				goto Repeat;
451 			sleep(5);
452 		}
453 		putscan(kbdinfd, esc1, sc);
454 		t = 30;
455 	}
456 Abort:
457 	chanfree(repeatc);
458 	threadexits("aborted");
459 
460 }
461 
462 
463 #define hasesc1(sc)	(((sc) > 0x47) || ((sc) == 0x38))
464 
465 static void
466 putmod(int fd, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc)
467 {
468 	/* BUG: Should be a single write */
469 	if((mods&mask) && !(omods&mask))
470 		putscan(fd, esc, sc);
471 	if(!(mods&mask) && (omods&mask))
472 		putscan(fd, esc, Keyup|sc);
473 }
474 
475 /*
476  * This routine diffs the state with the last known state
477  * and invents the scan codes that would have been sent
478  * by a non-usb keyboard in that case. This also requires supplying
479  * the extra esc1 byte as well as keyup flags.
480  * The aim is to allow future addition of other keycode pages
481  * for other keyboards.
482  */
483 static uchar
484 putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk)
485 {
486 	int i, j;
487 	uchar uk;
488 	int fd;
489 
490 	fd = f->in->fd;
491 	putmod(fd, buf[0], obuf[0], Mctrl, 0, SCctrl);
492 	putmod(fd, buf[0], obuf[0], (1<<Mlshift), 0, SClshift);
493 	putmod(fd, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift);
494 	putmod(fd, buf[0], obuf[0], Mcompose, 0, SCcompose);
495 	putmod(fd, buf[0], obuf[0], Maltgr, 1, SCcompose);
496 
497 	/* Report key downs */
498 	for(i = 2; i < n; i++){
499 		for(j = 2; j < n; j++)
500 			if(buf[i] == obuf[j])
501 			 	break;
502 		if(j == n && buf[i] != 0){
503 			dk = sctab[buf[i]];
504 			putscan(fd, hasesc1(dk), dk);
505 			startrepeat(f, hasesc1(dk), dk);
506 		}
507 	}
508 
509 	/* Report key ups */
510 	uk = 0;
511 	for(i = 2; i < n; i++){
512 		for(j = 2; j < n; j++)
513 			if(obuf[i] == buf[j])
514 				break;
515 		if(j == n && obuf[i] != 0){
516 			uk = sctab[obuf[i]];
517 			putscan(fd, hasesc1(uk), uk|Keyup);
518 		}
519 	}
520 	if(uk && (dk == 0 || dk == uk)){
521 		stoprepeat(f);
522 		dk = 0;
523 	}
524 	return dk;
525 }
526 
527 static int
528 kbdbusy(uchar* buf, int n)
529 {
530 	int i;
531 
532 	for(i = 1; i < n; i++)
533 		if(buf[i] == 0 || buf[i] != buf[0])
534 			return 0;
535 	return 1;
536 }
537 
538 static void
539 kbdwork(void *a)
540 {
541 	int c, i, kbdfd, nerrs;
542 	uchar dk, buf[64], lbuf[64];
543 	char err[128];
544 	KDev *f = a;
545 
546 	kbdfd = f->ep->dfd;
547 
548 	if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf)
549 		kbfatal(f, "weird maxpkt");
550 
551 	f->repeatc = chancreate(sizeof(ulong), 0);
552 	if(f->repeatc == nil)
553 		kbfatal(f, "chancreate failed");
554 
555 	proccreate(repeatproc, f, Stack);
556 	memset(lbuf, 0, sizeof lbuf);
557 	dk = nerrs = 0;
558 	for(;;){
559 		memset(buf, 0, sizeof buf);
560 		c = read(kbdfd, buf, f->ep->maxpkt);
561 		assert(f->dev != nil);
562 		assert(f->ep != nil);
563 		if(c < 0){
564 			rerrstr(err, sizeof(err));
565 			fprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
566 			if(strstr(err, "babble") != 0 && ++nerrs < 3){
567 				recoverkb(f);
568 				continue;
569 			}
570 		}
571 		if(c <= 0)
572 			kbfatal(f, nil);
573 		if(c < 3)
574 			continue;
575 		if(kbdbusy(buf + 2, c - 2))
576 			continue;
577 		if(usbdebug > 2 || kbdebug > 1){
578 			fprint(2, "kbd mod %x: ", buf[0]);
579 			for(i = 2; i < c; i++)
580 				fprint(2, "kc %x ", buf[i]);
581 			fprint(2, "\n");
582 		}
583 		dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
584 		memmove(lbuf, buf, c);
585 		nerrs = 0;
586 	}
587 }
588 
589 static void
590 freekdev(void *a)
591 {
592 	KDev *kd;
593 
594 	kd = a;
595 	if(kd->in != nil){
596 		qlock(&inlck);
597 		if(--kd->in->ref == 0){
598 			close(kd->in->fd);
599 			kd->in->fd = -1;
600 		}
601 		qunlock(&inlck);
602 	}
603 	dprint(2, "freekdev\n");
604 	free(kd);
605 }
606 
607 static void
608 kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd)
609 {
610 	uchar desc[128];
611 	int res;
612 
613 	qlock(&inlck);
614 	if(in->fd < 0){
615 		in->fd = open(in->name, OWRITE);
616 		if(in->fd < 0){
617 			fprint(2, "kb: %s: %r\n", in->name);
618 			qunlock(&inlck);
619 			return;
620 		}
621 	}
622 	in->ref++;	/* for kd->in = in */
623 	qunlock(&inlck);
624 	d->free = freekdev;
625 	kd->in = in;
626 	kd->dev = d;
627 	res = -1;
628 	kd->ep = openep(d, ep->id);
629 	if(kd->ep == nil){
630 		fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id);
631 		return;
632 	}
633 	if(!kd->bootp)
634 		res= setfirstconfig(kd, ep->id, desc, sizeof desc);
635 	if(res > 0)
636 		res = parsereportdesc(&kd->templ, desc, sizeof desc);
637 	/* if we could not set the first config, we give up */
638 	if(kd->bootp || res < 0){
639 		kd->bootp = 1;
640 		if(setbootproto(kd, ep->id, nil, 0) < 0){
641 			fprint(2, "kb: %s: bootproto: %r\n", d->dir);
642 			return;
643 		}
644 	}else if(kbdebug)
645 		dumpreport(&kd->templ);
646 	if(opendevdata(kd->ep, OREAD) < 0){
647 		fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir);
648 		closedev(kd->ep);
649 		kd->ep = nil;
650 		return;
651 	}
652 
653 	incref(d);
654 	proccreate(f, kd, Stack);
655 }
656 
657 static int
658 usage(void)
659 {
660 	werrstr("usage: usb/kb [-bdkm] [-a n] [-N nb]");
661 	return -1;
662 }
663 
664 int
665 kbmain(Dev *d, int argc, char* argv[])
666 {
667 	int bootp, i, kena, pena, accel, devid;
668 	Ep *ep;
669 	KDev *kd;
670 	Usbdev *ud;
671 
672 	kena = pena = 1;
673 	bootp = 0;
674 	accel = 0;
675 	devid = d->id;
676 	ARGBEGIN{
677 	case 'a':
678 		accel = strtol(EARGF(usage()), nil, 0);
679 		break;
680 	case 'd':
681 		kbdebug++;
682 		break;
683 	case 'k':
684 		kena = 1;
685 		pena = 0;
686 		break;
687 	case 'm':
688 		kena = 0;
689 		pena = 1;
690 		break;
691 	case 'N':
692 		devid = atoi(EARGF(usage()));		/* ignore dev number */
693 		break;
694 	case 'b':
695 		bootp++;
696 		break;
697 	default:
698 		return usage();
699 	}ARGEND;
700 	if(argc != 0)
701 		return usage();
702 	USED(devid);
703 	ud = d->usb;
704 	d->aux = nil;
705 	dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref);
706 
707 	if(kena)
708 		for(i = 0; i < nelem(ud->ep); i++)
709 			if((ep = ud->ep[i]) == nil)
710 				break;
711 			else if(ep->iface->csp == KbdCSP)
712 				bootp = 1;
713 
714 	for(i = 0; i < nelem(ud->ep); i++){
715 		if((ep = ud->ep[i]) == nil)
716 			break;
717 		if(kena && ep->type == Eintr && ep->dir == Ein &&
718 		    ep->iface->csp == KbdCSP){
719 			kd = d->aux = emallocz(sizeof(KDev), 1);
720 			kd->accel = 0;
721 			kd->bootp = 1;
722 			kbstart(d, ep, &kbdin, kbdwork, kd);
723 		}
724 		if(pena && ep->type == Eintr && ep->dir == Ein &&
725 		    ep->iface->csp == PtrCSP){
726 			kd = d->aux = emallocz(sizeof(KDev), 1);
727 			kd->accel = accel;
728 			kd->bootp = bootp;
729 			kbstart(d, ep, &ptrin, ptrwork, kd);
730 		}
731 	}
732 	return 0;
733 }
734