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 */ 39*9b7bf7dfSDavid 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 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 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; 155*9b7bf7dfSDavid 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); 164*9b7bf7dfSDavid du Colombier if(nr <= 0) 165d40255d8SDavid du Colombier return -1; 166*9b7bf7dfSDavid 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 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 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 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 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 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 292*9b7bf7dfSDavid 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 |= 0x10; 303d40255d8SDavid du Colombier if(c > 3 && hidifcval(&kd->templ, KindWheel, 0) < 0) /* down */ 304d40255d8SDavid du Colombier b |= 0x08; 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 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 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 } 380*9b7bf7dfSDavid 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 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 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 411*9b7bf7dfSDavid du Colombier putscan(KDev *f, uchar esc, uchar sc) 41283030dd5SDavid du Colombier { 413*9b7bf7dfSDavid du Colombier int kbinfd; 414906943f9SDavid du Colombier uchar s[2] = {SCesc1, 0}; 41583030dd5SDavid du Colombier 416*9b7bf7dfSDavid du Colombier kbinfd = f->in->fd; 41783030dd5SDavid du Colombier if(sc == 0x41){ 418*9b7bf7dfSDavid du Colombier f->debug += 2; 41983030dd5SDavid du Colombier return; 42083030dd5SDavid du Colombier } 42183030dd5SDavid du Colombier if(sc == 0x42){ 422*9b7bf7dfSDavid du Colombier f->debug = 0; 42383030dd5SDavid du Colombier return; 42483030dd5SDavid du Colombier } 425*9b7bf7dfSDavid 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 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 } 466*9b7bf7dfSDavid 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 47683030dd5SDavid du Colombier #define hasesc1(sc) (((sc) > 0x47) || ((sc) == 0x38)) 47783030dd5SDavid du Colombier 47883030dd5SDavid du Colombier static void 479*9b7bf7dfSDavid 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)) 483*9b7bf7dfSDavid du Colombier putscan(f, esc, sc); 48483030dd5SDavid du Colombier if(!(mods&mask) && (omods&mask)) 485*9b7bf7dfSDavid 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 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 502*9b7bf7dfSDavid du Colombier putmod(f, buf[0], obuf[0], Mctrl, 0, SCctrl); 503*9b7bf7dfSDavid du Colombier putmod(f, buf[0], obuf[0], (1<<Mlshift), 0, SClshift); 504*9b7bf7dfSDavid du Colombier putmod(f, buf[0], obuf[0], (1<<Mrshift), 0, SCrshift); 505*9b7bf7dfSDavid du Colombier putmod(f, buf[0], obuf[0], Mcompose, 0, SCcompose); 506*9b7bf7dfSDavid 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]]; 515*9b7bf7dfSDavid 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]]; 528*9b7bf7dfSDavid 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 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 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; 589*9b7bf7dfSDavid 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 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 620d40255d8SDavid du Colombier kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), KDev *kd) 621906943f9SDavid du Colombier { 622d40255d8SDavid du Colombier uchar desc[128]; 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 } 669*9b7bf7dfSDavid 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 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 690906943f9SDavid du Colombier kbmain(Dev *d, int argc, char* argv[]) 69183030dd5SDavid du Colombier { 692*9b7bf7dfSDavid 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; 700*9b7bf7dfSDavid 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': 707*9b7bf7dfSDavid 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) 742906943f9SDavid du Colombier break; 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; 748*9b7bf7dfSDavid 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; 756*9b7bf7dfSDavid 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