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