kb.c 13 KB

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