1 /* $NetBSD: btuart.c,v 1.23 2009/05/12 12:10:46 cegger Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, 2007 KIYOHARA Takashi 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: btuart.c,v 1.23 2009/05/12 12:10:46 cegger Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/conf.h> 34 #include <sys/device.h> 35 #include <sys/errno.h> 36 #include <sys/fcntl.h> 37 #include <sys/kauth.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/mbuf.h> 41 #include <sys/proc.h> 42 #include <sys/syslimits.h> 43 #include <sys/systm.h> 44 #include <sys/tty.h> 45 46 #include <sys/bus.h> 47 #include <sys/intr.h> 48 49 #include <netbt/bluetooth.h> 50 #include <netbt/hci.h> 51 52 #include "ioconf.h" 53 54 struct btuart_softc { 55 device_t sc_dev; 56 struct tty * sc_tp; /* tty pointer */ 57 58 bool sc_enabled; /* device is enabled */ 59 struct hci_unit *sc_unit; /* Bluetooth HCI handle */ 60 struct bt_stats sc_stats; 61 62 int sc_state; /* receive state */ 63 int sc_want; /* how much we want */ 64 struct mbuf * sc_rxp; /* incoming packet */ 65 66 bool sc_xmit; /* transmit is active */ 67 struct mbuf * sc_txp; /* outgoing packet */ 68 69 /* transmit queues */ 70 MBUFQ_HEAD() sc_cmdq; 71 MBUFQ_HEAD() sc_aclq; 72 MBUFQ_HEAD() sc_scoq; 73 }; 74 75 /* sc_state */ 76 #define BTUART_RECV_PKT_TYPE 0 /* packet type */ 77 #define BTUART_RECV_ACL_HDR 1 /* acl header */ 78 #define BTUART_RECV_SCO_HDR 2 /* sco header */ 79 #define BTUART_RECV_EVENT_HDR 3 /* event header */ 80 #define BTUART_RECV_ACL_DATA 4 /* acl packet data */ 81 #define BTUART_RECV_SCO_DATA 5 /* sco packet data */ 82 #define BTUART_RECV_EVENT_DATA 6 /* event packet data */ 83 84 void btuartattach(int); 85 static int btuart_match(device_t, cfdata_t, void *); 86 static void btuart_attach(device_t, device_t, void *); 87 static int btuart_detach(device_t, int); 88 89 static int btuartopen(dev_t, struct tty *); 90 static int btuartclose(struct tty *, int); 91 static int btuartioctl(struct tty *, u_long, void *, int, struct lwp *); 92 static int btuartinput(int, struct tty *); 93 static int btuartstart(struct tty *); 94 95 static int btuart_enable(device_t); 96 static void btuart_disable(device_t); 97 static void btuart_output_cmd(device_t, struct mbuf *); 98 static void btuart_output_acl(device_t, struct mbuf *); 99 static void btuart_output_sco(device_t, struct mbuf *); 100 static void btuart_stats(device_t, struct bt_stats *, int); 101 102 /* 103 * It doesn't need to be exported, as only btuartattach() uses it, 104 * but there's no "official" way to make it static. 105 */ 106 CFATTACH_DECL_NEW(btuart, sizeof(struct btuart_softc), 107 btuart_match, btuart_attach, btuart_detach, NULL); 108 109 static struct linesw btuart_disc = { 110 .l_name = "btuart", 111 .l_open = btuartopen, 112 .l_close = btuartclose, 113 .l_read = ttyerrio, 114 .l_write = ttyerrio, 115 .l_ioctl = btuartioctl, 116 .l_rint = btuartinput, 117 .l_start = btuartstart, 118 .l_modem = ttymodem, 119 .l_poll = ttyerrpoll, 120 }; 121 122 static const struct hci_if btuart_hci = { 123 .enable = btuart_enable, 124 .disable = btuart_disable, 125 .output_cmd = btuart_output_cmd, 126 .output_acl = btuart_output_acl, 127 .output_sco = btuart_output_sco, 128 .get_stats = btuart_stats, 129 .ipl = IPL_TTY, 130 }; 131 132 /***************************************************************************** 133 * 134 * autoconf(9) functions 135 */ 136 137 /* 138 * pseudo-device attach routine. 139 */ 140 void 141 btuartattach(int num __unused) 142 { 143 int error; 144 145 error = ttyldisc_attach(&btuart_disc); 146 if (error) { 147 aprint_error("%s: unable to register line discipline, " 148 "error = %d\n", btuart_cd.cd_name, error); 149 150 return; 151 } 152 153 error = config_cfattach_attach(btuart_cd.cd_name, &btuart_ca); 154 if (error) { 155 aprint_error("%s: unable to register cfattach, error = %d\n", 156 btuart_cd.cd_name, error); 157 158 config_cfdriver_detach(&btuart_cd); 159 (void) ttyldisc_detach(&btuart_disc); 160 } 161 } 162 163 /* 164 * Autoconf match routine. 165 */ 166 static int 167 btuart_match(device_t self __unused, cfdata_t cfdata __unused, 168 void *arg __unused) 169 { 170 171 /* pseudo-device; always present */ 172 return 1; 173 } 174 175 /* 176 * Autoconf attach routine. 177 * Called by config_attach_pseudo(9) when we open the line discipline. 178 */ 179 static void 180 btuart_attach(device_t parent __unused, device_t self, void *aux __unused) 181 { 182 struct btuart_softc *sc = device_private(self); 183 184 sc->sc_dev = self; 185 186 MBUFQ_INIT(&sc->sc_cmdq); 187 MBUFQ_INIT(&sc->sc_aclq); 188 MBUFQ_INIT(&sc->sc_scoq); 189 190 /* Attach Bluetooth unit */ 191 sc->sc_unit = hci_attach(&btuart_hci, self, 0); 192 if (sc->sc_unit == NULL) 193 aprint_error_dev(self, "HCI attach failed\n"); 194 } 195 196 /* 197 * Autoconf detach routine. 198 * Called when we close the line discipline. 199 */ 200 static int 201 btuart_detach(device_t self, int flags __unused) 202 { 203 struct btuart_softc *sc = device_private(self); 204 205 btuart_disable(self); 206 207 if (sc->sc_unit) { 208 hci_detach(sc->sc_unit); 209 sc->sc_unit = NULL; 210 } 211 212 return 0; 213 } 214 215 /***************************************************************************** 216 * 217 * Line discipline functions. 218 */ 219 220 static int 221 btuartopen(dev_t devno __unused, struct tty *tp) 222 { 223 struct btuart_softc *sc; 224 device_t dev; 225 cfdata_t cfdata; 226 struct lwp *l = curlwp; /* XXX */ 227 int error, unit, s; 228 229 error = kauth_authorize_device(l->l_cred, KAUTH_DEVICE_BLUETOOTH_BTUART, 230 KAUTH_ARG(KAUTH_REQ_DEVICE_BLUETOOTH_BTUART_ADD), NULL, NULL, NULL); 231 if (error) 232 return (error); 233 234 s = spltty(); 235 236 if (tp->t_linesw == &btuart_disc) { 237 sc = tp->t_sc; 238 if (sc != NULL) { 239 splx(s); 240 return EBUSY; 241 } 242 } 243 244 KASSERT(tp->t_oproc != NULL); 245 246 cfdata = malloc(sizeof(struct cfdata), M_DEVBUF, M_WAITOK); 247 for (unit = 0; unit < btuart_cd.cd_ndevs; unit++) 248 if (device_lookup(&btuart_cd, unit) == NULL) 249 break; 250 251 cfdata->cf_name = btuart_cd.cd_name; 252 cfdata->cf_atname = btuart_cd.cd_name; 253 cfdata->cf_unit = unit; 254 cfdata->cf_fstate = FSTATE_STAR; 255 256 dev = config_attach_pseudo(cfdata); 257 if (dev == NULL) { 258 free(cfdata, M_DEVBUF); 259 splx(s); 260 return EIO; 261 } 262 sc = device_private(dev); 263 264 aprint_normal_dev(dev, "major %llu minor %llu\n", 265 (unsigned long long)major(tp->t_dev), 266 (unsigned long long)minor(tp->t_dev)); 267 268 sc->sc_tp = tp; 269 tp->t_sc = sc; 270 271 mutex_spin_enter(&tty_lock); 272 ttyflush(tp, FREAD | FWRITE); 273 mutex_spin_exit(&tty_lock); 274 275 splx(s); 276 277 return 0; 278 } 279 280 static int 281 btuartclose(struct tty *tp, int flag __unused) 282 { 283 struct btuart_softc *sc = tp->t_sc; 284 cfdata_t cfdata; 285 int s; 286 287 s = spltty(); 288 289 mutex_spin_enter(&tty_lock); 290 ttyflush(tp, FREAD | FWRITE); 291 mutex_spin_exit(&tty_lock); /* XXX */ 292 293 ttyldisc_release(tp->t_linesw); 294 tp->t_linesw = ttyldisc_default(); 295 296 if (sc != NULL) { 297 tp->t_sc = NULL; 298 if (sc->sc_tp == tp) { 299 cfdata = device_cfdata(sc->sc_dev); 300 config_detach(sc->sc_dev, 0); 301 free(cfdata, M_DEVBUF); 302 } 303 } 304 305 splx(s); 306 307 return 0; 308 } 309 310 static int 311 btuartioctl(struct tty *tp, u_long cmd, void *data __unused, 312 int flag __unused, struct lwp *l __unused) 313 { 314 struct btuart_softc *sc = tp->t_sc; 315 int error; 316 317 if (sc == NULL || tp != sc->sc_tp) 318 return EPASSTHROUGH; 319 320 switch(cmd) { 321 default: 322 error = EPASSTHROUGH; 323 break; 324 } 325 326 return error; 327 } 328 329 static int 330 btuartinput(int c, struct tty *tp) 331 { 332 struct btuart_softc *sc = tp->t_sc; 333 struct mbuf *m = sc->sc_rxp; 334 int space = 0; 335 336 if (!sc->sc_enabled) 337 return 0; 338 339 c &= TTY_CHARMASK; 340 341 /* If we already started a packet, find the trailing end of it. */ 342 if (m) { 343 while (m->m_next) 344 m = m->m_next; 345 346 space = M_TRAILINGSPACE(m); 347 } 348 349 if (space == 0) { 350 if (m == NULL) { 351 /* new packet */ 352 MGETHDR(m, M_DONTWAIT, MT_DATA); 353 if (m == NULL) { 354 aprint_error_dev(sc->sc_dev, 355 "out of memory\n"); 356 sc->sc_stats.err_rx++; 357 return 0; /* (lost sync) */ 358 } 359 360 sc->sc_rxp = m; 361 m->m_pkthdr.len = m->m_len = 0; 362 space = MHLEN; 363 364 sc->sc_state = BTUART_RECV_PKT_TYPE; 365 sc->sc_want = 1; 366 } else { 367 /* extend mbuf */ 368 MGET(m->m_next, M_DONTWAIT, MT_DATA); 369 if (m->m_next == NULL) { 370 aprint_error_dev(sc->sc_dev, 371 "out of memory\n"); 372 sc->sc_stats.err_rx++; 373 return 0; /* (lost sync) */ 374 } 375 376 m = m->m_next; 377 m->m_len = 0; 378 space = MLEN; 379 380 if (sc->sc_want > MINCLSIZE) { 381 MCLGET(m, M_DONTWAIT); 382 if (m->m_flags & M_EXT) 383 space = MCLBYTES; 384 } 385 } 386 } 387 388 mtod(m, uint8_t *)[m->m_len++] = c; 389 sc->sc_rxp->m_pkthdr.len++; 390 sc->sc_stats.byte_rx++; 391 392 sc->sc_want--; 393 if (sc->sc_want > 0) 394 return 0; /* want more */ 395 396 switch (sc->sc_state) { 397 case BTUART_RECV_PKT_TYPE: /* Got packet type */ 398 399 switch (c) { 400 case HCI_ACL_DATA_PKT: 401 sc->sc_state = BTUART_RECV_ACL_HDR; 402 sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; 403 break; 404 405 case HCI_SCO_DATA_PKT: 406 sc->sc_state = BTUART_RECV_SCO_HDR; 407 sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; 408 break; 409 410 case HCI_EVENT_PKT: 411 sc->sc_state = BTUART_RECV_EVENT_HDR; 412 sc->sc_want = sizeof(hci_event_hdr_t) - 1; 413 break; 414 415 default: 416 aprint_error_dev(sc->sc_dev, 417 "Unknown packet type=%#x!\n", c); 418 sc->sc_stats.err_rx++; 419 m_freem(sc->sc_rxp); 420 sc->sc_rxp = NULL; 421 return 0; /* (lost sync) */ 422 } 423 424 break; 425 426 /* 427 * we assume (correctly of course :) that the packet headers all fit 428 * into a single pkthdr mbuf 429 */ 430 case BTUART_RECV_ACL_HDR: /* Got ACL Header */ 431 sc->sc_state = BTUART_RECV_ACL_DATA; 432 sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; 433 sc->sc_want = le16toh(sc->sc_want); 434 break; 435 436 case BTUART_RECV_SCO_HDR: /* Got SCO Header */ 437 sc->sc_state = BTUART_RECV_SCO_DATA; 438 sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; 439 break; 440 441 case BTUART_RECV_EVENT_HDR: /* Got Event Header */ 442 sc->sc_state = BTUART_RECV_EVENT_DATA; 443 sc->sc_want = mtod(m, hci_event_hdr_t *)->length; 444 break; 445 446 case BTUART_RECV_ACL_DATA: /* ACL Packet Complete */ 447 if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) 448 sc->sc_stats.err_rx++; 449 450 sc->sc_stats.acl_rx++; 451 sc->sc_rxp = m = NULL; 452 break; 453 454 case BTUART_RECV_SCO_DATA: /* SCO Packet Complete */ 455 if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) 456 sc->sc_stats.err_rx++; 457 458 sc->sc_stats.sco_rx++; 459 sc->sc_rxp = m = NULL; 460 break; 461 462 case BTUART_RECV_EVENT_DATA: /* Event Packet Complete */ 463 if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) 464 sc->sc_stats.err_rx++; 465 466 sc->sc_stats.evt_rx++; 467 sc->sc_rxp = m = NULL; 468 break; 469 470 default: 471 panic("%s: invalid state %d!\n", 472 device_xname(sc->sc_dev), sc->sc_state); 473 } 474 475 return 0; 476 } 477 478 static int 479 btuartstart(struct tty *tp) 480 { 481 struct btuart_softc *sc = tp->t_sc; 482 struct mbuf *m; 483 int count, rlen; 484 uint8_t *rptr; 485 486 if (!sc->sc_enabled) 487 return 0; 488 489 m = sc->sc_txp; 490 if (m == NULL) { 491 if (MBUFQ_FIRST(&sc->sc_cmdq)) { 492 MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 493 sc->sc_stats.cmd_tx++; 494 } else if (MBUFQ_FIRST(&sc->sc_scoq)) { 495 MBUFQ_DEQUEUE(&sc->sc_scoq, m); 496 sc->sc_stats.sco_tx++; 497 } else if (MBUFQ_FIRST(&sc->sc_aclq)) { 498 MBUFQ_DEQUEUE(&sc->sc_aclq, m); 499 sc->sc_stats.acl_tx++; 500 } else { 501 sc->sc_xmit = false; 502 return 0; /* no more to send */ 503 } 504 505 sc->sc_txp = m; 506 sc->sc_xmit = true; 507 } 508 509 count = 0; 510 rlen = 0; 511 rptr = mtod(m, uint8_t *); 512 513 for(;;) { 514 if (rlen >= m->m_len) { 515 m = m->m_next; 516 if (m == NULL) { 517 m = sc->sc_txp; 518 sc->sc_txp = NULL; 519 520 if (M_GETCTX(m, void *) == NULL) 521 m_freem(m); 522 else if (!hci_complete_sco(sc->sc_unit, m)) 523 sc->sc_stats.err_tx++; 524 525 break; 526 } 527 528 rlen = 0; 529 rptr = mtod(m, uint8_t *); 530 continue; 531 } 532 533 if (putc(*rptr++, &tp->t_outq) < 0) { 534 m_adj(m, rlen); 535 break; 536 } 537 rlen++; 538 count++; 539 } 540 541 sc->sc_stats.byte_tx += count; 542 543 if (tp->t_outq.c_cc != 0) 544 (*tp->t_oproc)(tp); 545 546 return 0; 547 } 548 549 /***************************************************************************** 550 * 551 * bluetooth(9) functions 552 */ 553 554 static int 555 btuart_enable(device_t self) 556 { 557 struct btuart_softc *sc = device_private(self); 558 int s; 559 560 if (sc->sc_enabled) 561 return 0; 562 563 s = spltty(); 564 565 sc->sc_enabled = true; 566 sc->sc_xmit = false; 567 568 splx(s); 569 570 return 0; 571 } 572 573 static void 574 btuart_disable(device_t self) 575 { 576 struct btuart_softc *sc = device_private(self); 577 int s; 578 579 if (!sc->sc_enabled) 580 return; 581 582 s = spltty(); 583 584 if (sc->sc_rxp) { 585 m_freem(sc->sc_rxp); 586 sc->sc_rxp = NULL; 587 } 588 589 if (sc->sc_txp) { 590 m_freem(sc->sc_txp); 591 sc->sc_txp = NULL; 592 } 593 594 MBUFQ_DRAIN(&sc->sc_cmdq); 595 MBUFQ_DRAIN(&sc->sc_aclq); 596 MBUFQ_DRAIN(&sc->sc_scoq); 597 598 sc->sc_enabled = false; 599 600 splx(s); 601 } 602 603 static void 604 btuart_output_cmd(device_t self, struct mbuf *m) 605 { 606 struct btuart_softc *sc = device_private(self); 607 int s; 608 609 KASSERT(sc->sc_enabled); 610 611 M_SETCTX(m, NULL); 612 613 s = spltty(); 614 MBUFQ_ENQUEUE(&sc->sc_cmdq, m); 615 if (!sc->sc_xmit) 616 btuartstart(sc->sc_tp); 617 618 splx(s); 619 } 620 621 static void 622 btuart_output_acl(device_t self, struct mbuf *m) 623 { 624 struct btuart_softc *sc = device_private(self); 625 int s; 626 627 KASSERT(sc->sc_enabled); 628 629 M_SETCTX(m, NULL); 630 631 s = spltty(); 632 MBUFQ_ENQUEUE(&sc->sc_aclq, m); 633 if (!sc->sc_xmit) 634 btuartstart(sc->sc_tp); 635 636 splx(s); 637 } 638 639 static void 640 btuart_output_sco(device_t self, struct mbuf *m) 641 { 642 struct btuart_softc *sc = device_private(self); 643 int s; 644 645 KASSERT(sc->sc_enabled); 646 647 s = spltty(); 648 MBUFQ_ENQUEUE(&sc->sc_scoq, m); 649 if (!sc->sc_xmit) 650 btuartstart(sc->sc_tp); 651 652 splx(s); 653 } 654 655 static void 656 btuart_stats(device_t self, struct bt_stats *dest, int flush) 657 { 658 struct btuart_softc *sc = device_private(self); 659 int s; 660 661 s = spltty(); 662 663 memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); 664 665 if (flush) 666 memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); 667 668 splx(s); 669 } 670