1 /* $NetBSD: btuart.c,v 1.20 2009/01/11 02:45:51 christos 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.20 2009/01/11 02:45:51 christos 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, struct cfdata *, 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, struct cfdata *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 struct cfdata *cfdata; 226 struct lwp *l = curlwp; /* XXX */ 227 int error, unit, s; 228 229 if ((error = kauth_authorize_device_tty(l->l_cred, 230 KAUTH_GENERIC_ISSUSER, tp)) != 0) 231 return error; 232 233 s = spltty(); 234 235 if (tp->t_linesw == &btuart_disc) { 236 sc = tp->t_sc; 237 if (sc != NULL) { 238 splx(s); 239 return EBUSY; 240 } 241 } 242 243 KASSERT(tp->t_oproc != NULL); 244 245 cfdata = malloc(sizeof(struct cfdata), M_DEVBUF, M_WAITOK); 246 for (unit = 0; unit < btuart_cd.cd_ndevs; unit++) 247 if (device_lookup(&btuart_cd, unit) == NULL) 248 break; 249 250 cfdata->cf_name = btuart_cd.cd_name; 251 cfdata->cf_atname = btuart_cd.cd_name; 252 cfdata->cf_unit = unit; 253 cfdata->cf_fstate = FSTATE_STAR; 254 255 dev = config_attach_pseudo(cfdata); 256 if (dev == NULL) { 257 free(cfdata, M_DEVBUF); 258 splx(s); 259 return EIO; 260 } 261 sc = device_private(dev); 262 263 aprint_normal_dev(dev, "major %llu minor %llu\n", 264 (unsigned long long)major(tp->t_dev), 265 (unsigned long long)minor(tp->t_dev)); 266 267 sc->sc_tp = tp; 268 tp->t_sc = sc; 269 270 mutex_spin_enter(&tty_lock); 271 ttyflush(tp, FREAD | FWRITE); 272 mutex_spin_exit(&tty_lock); 273 274 splx(s); 275 276 return 0; 277 } 278 279 static int 280 btuartclose(struct tty *tp, int flag __unused) 281 { 282 struct btuart_softc *sc = tp->t_sc; 283 struct cfdata *cfdata; 284 int s; 285 286 s = spltty(); 287 288 mutex_spin_enter(&tty_lock); 289 ttyflush(tp, FREAD | FWRITE); 290 mutex_spin_exit(&tty_lock); /* XXX */ 291 292 ttyldisc_release(tp->t_linesw); 293 tp->t_linesw = ttyldisc_default(); 294 295 if (sc != NULL) { 296 tp->t_sc = NULL; 297 if (sc->sc_tp == tp) { 298 cfdata = device_cfdata(sc->sc_dev); 299 config_detach(sc->sc_dev, 0); 300 free(cfdata, M_DEVBUF); 301 } 302 } 303 304 splx(s); 305 306 return 0; 307 } 308 309 static int 310 btuartioctl(struct tty *tp, u_long cmd, void *data __unused, 311 int flag __unused, struct lwp *l __unused) 312 { 313 struct btuart_softc *sc = tp->t_sc; 314 int error; 315 316 if (sc == NULL || tp != sc->sc_tp) 317 return EPASSTHROUGH; 318 319 switch(cmd) { 320 default: 321 error = EPASSTHROUGH; 322 break; 323 } 324 325 return error; 326 } 327 328 static int 329 btuartinput(int c, struct tty *tp) 330 { 331 struct btuart_softc *sc = tp->t_sc; 332 struct mbuf *m = sc->sc_rxp; 333 int space = 0; 334 335 if (!sc->sc_enabled) 336 return 0; 337 338 c &= TTY_CHARMASK; 339 340 /* If we already started a packet, find the trailing end of it. */ 341 if (m) { 342 while (m->m_next) 343 m = m->m_next; 344 345 space = M_TRAILINGSPACE(m); 346 } 347 348 if (space == 0) { 349 if (m == NULL) { 350 /* new packet */ 351 MGETHDR(m, M_DONTWAIT, MT_DATA); 352 if (m == NULL) { 353 aprint_error_dev(sc->sc_dev, 354 "out of memory\n"); 355 sc->sc_stats.err_rx++; 356 return 0; /* (lost sync) */ 357 } 358 359 sc->sc_rxp = m; 360 m->m_pkthdr.len = m->m_len = 0; 361 space = MHLEN; 362 363 sc->sc_state = BTUART_RECV_PKT_TYPE; 364 sc->sc_want = 1; 365 } else { 366 /* extend mbuf */ 367 MGET(m->m_next, M_DONTWAIT, MT_DATA); 368 if (m->m_next == NULL) { 369 aprint_error_dev(sc->sc_dev, 370 "out of memory\n"); 371 sc->sc_stats.err_rx++; 372 return 0; /* (lost sync) */ 373 } 374 375 m = m->m_next; 376 m->m_len = 0; 377 space = MLEN; 378 379 if (sc->sc_want > MINCLSIZE) { 380 MCLGET(m, M_DONTWAIT); 381 if (m->m_flags & M_EXT) 382 space = MCLBYTES; 383 } 384 } 385 } 386 387 mtod(m, uint8_t *)[m->m_len++] = c; 388 sc->sc_rxp->m_pkthdr.len++; 389 sc->sc_stats.byte_rx++; 390 391 sc->sc_want--; 392 if (sc->sc_want > 0) 393 return 0; /* want more */ 394 395 switch (sc->sc_state) { 396 case BTUART_RECV_PKT_TYPE: /* Got packet type */ 397 398 switch (c) { 399 case HCI_ACL_DATA_PKT: 400 sc->sc_state = BTUART_RECV_ACL_HDR; 401 sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; 402 break; 403 404 case HCI_SCO_DATA_PKT: 405 sc->sc_state = BTUART_RECV_SCO_HDR; 406 sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; 407 break; 408 409 case HCI_EVENT_PKT: 410 sc->sc_state = BTUART_RECV_EVENT_HDR; 411 sc->sc_want = sizeof(hci_event_hdr_t) - 1; 412 break; 413 414 default: 415 aprint_error_dev(sc->sc_dev, 416 "Unknown packet type=%#x!\n", c); 417 sc->sc_stats.err_rx++; 418 m_freem(sc->sc_rxp); 419 sc->sc_rxp = NULL; 420 return 0; /* (lost sync) */ 421 } 422 423 break; 424 425 /* 426 * we assume (correctly of course :) that the packet headers all fit 427 * into a single pkthdr mbuf 428 */ 429 case BTUART_RECV_ACL_HDR: /* Got ACL Header */ 430 sc->sc_state = BTUART_RECV_ACL_DATA; 431 sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; 432 sc->sc_want = le16toh(sc->sc_want); 433 break; 434 435 case BTUART_RECV_SCO_HDR: /* Got SCO Header */ 436 sc->sc_state = BTUART_RECV_SCO_DATA; 437 sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; 438 break; 439 440 case BTUART_RECV_EVENT_HDR: /* Got Event Header */ 441 sc->sc_state = BTUART_RECV_EVENT_DATA; 442 sc->sc_want = mtod(m, hci_event_hdr_t *)->length; 443 break; 444 445 case BTUART_RECV_ACL_DATA: /* ACL Packet Complete */ 446 if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) 447 sc->sc_stats.err_rx++; 448 449 sc->sc_stats.acl_rx++; 450 sc->sc_rxp = m = NULL; 451 break; 452 453 case BTUART_RECV_SCO_DATA: /* SCO Packet Complete */ 454 if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) 455 sc->sc_stats.err_rx++; 456 457 sc->sc_stats.sco_rx++; 458 sc->sc_rxp = m = NULL; 459 break; 460 461 case BTUART_RECV_EVENT_DATA: /* Event Packet Complete */ 462 if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) 463 sc->sc_stats.err_rx++; 464 465 sc->sc_stats.evt_rx++; 466 sc->sc_rxp = m = NULL; 467 break; 468 469 default: 470 panic("%s: invalid state %d!\n", 471 device_xname(sc->sc_dev), sc->sc_state); 472 } 473 474 return 0; 475 } 476 477 static int 478 btuartstart(struct tty *tp) 479 { 480 struct btuart_softc *sc = tp->t_sc; 481 struct mbuf *m; 482 int count, rlen; 483 uint8_t *rptr; 484 485 if (!sc->sc_enabled) 486 return 0; 487 488 m = sc->sc_txp; 489 if (m == NULL) { 490 if (MBUFQ_FIRST(&sc->sc_cmdq)) { 491 MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 492 sc->sc_stats.cmd_tx++; 493 } else if (MBUFQ_FIRST(&sc->sc_scoq)) { 494 MBUFQ_DEQUEUE(&sc->sc_scoq, m); 495 sc->sc_stats.sco_tx++; 496 } else if (MBUFQ_FIRST(&sc->sc_aclq)) { 497 MBUFQ_DEQUEUE(&sc->sc_aclq, m); 498 sc->sc_stats.acl_tx++; 499 } else { 500 sc->sc_xmit = false; 501 return 0; /* no more to send */ 502 } 503 504 sc->sc_txp = m; 505 sc->sc_xmit = true; 506 } 507 508 count = 0; 509 rlen = 0; 510 rptr = mtod(m, uint8_t *); 511 512 for(;;) { 513 if (rlen >= m->m_len) { 514 m = m->m_next; 515 if (m == NULL) { 516 m = sc->sc_txp; 517 sc->sc_txp = NULL; 518 519 if (M_GETCTX(m, void *) == NULL) 520 m_freem(m); 521 else if (!hci_complete_sco(sc->sc_unit, m)) 522 sc->sc_stats.err_tx++; 523 524 break; 525 } 526 527 rlen = 0; 528 rptr = mtod(m, uint8_t *); 529 continue; 530 } 531 532 if (putc(*rptr++, &tp->t_outq) < 0) { 533 m_adj(m, rlen); 534 break; 535 } 536 rlen++; 537 count++; 538 } 539 540 sc->sc_stats.byte_tx += count; 541 542 if (tp->t_outq.c_cc != 0) 543 (*tp->t_oproc)(tp); 544 545 return 0; 546 } 547 548 /***************************************************************************** 549 * 550 * bluetooth(9) functions 551 */ 552 553 static int 554 btuart_enable(device_t self) 555 { 556 struct btuart_softc *sc = device_private(self); 557 int s; 558 559 if (sc->sc_enabled) 560 return 0; 561 562 s = spltty(); 563 564 sc->sc_enabled = true; 565 sc->sc_xmit = false; 566 567 splx(s); 568 569 return 0; 570 } 571 572 static void 573 btuart_disable(device_t self) 574 { 575 struct btuart_softc *sc = device_private(self); 576 int s; 577 578 if (!sc->sc_enabled) 579 return; 580 581 s = spltty(); 582 583 if (sc->sc_rxp) { 584 m_freem(sc->sc_rxp); 585 sc->sc_rxp = NULL; 586 } 587 588 if (sc->sc_txp) { 589 m_freem(sc->sc_txp); 590 sc->sc_txp = NULL; 591 } 592 593 MBUFQ_DRAIN(&sc->sc_cmdq); 594 MBUFQ_DRAIN(&sc->sc_aclq); 595 MBUFQ_DRAIN(&sc->sc_scoq); 596 597 sc->sc_enabled = false; 598 599 splx(s); 600 } 601 602 static void 603 btuart_output_cmd(device_t self, struct mbuf *m) 604 { 605 struct btuart_softc *sc = device_private(self); 606 int s; 607 608 KASSERT(sc->sc_enabled); 609 610 M_SETCTX(m, NULL); 611 612 s = spltty(); 613 MBUFQ_ENQUEUE(&sc->sc_cmdq, m); 614 if (!sc->sc_xmit) 615 btuartstart(sc->sc_tp); 616 617 splx(s); 618 } 619 620 static void 621 btuart_output_acl(device_t self, struct mbuf *m) 622 { 623 struct btuart_softc *sc = device_private(self); 624 int s; 625 626 KASSERT(sc->sc_enabled); 627 628 M_SETCTX(m, NULL); 629 630 s = spltty(); 631 MBUFQ_ENQUEUE(&sc->sc_aclq, m); 632 if (!sc->sc_xmit) 633 btuartstart(sc->sc_tp); 634 635 splx(s); 636 } 637 638 static void 639 btuart_output_sco(device_t self, struct mbuf *m) 640 { 641 struct btuart_softc *sc = device_private(self); 642 int s; 643 644 KASSERT(sc->sc_enabled); 645 646 s = spltty(); 647 MBUFQ_ENQUEUE(&sc->sc_scoq, m); 648 if (!sc->sc_xmit) 649 btuartstart(sc->sc_tp); 650 651 splx(s); 652 } 653 654 static void 655 btuart_stats(device_t self, struct bt_stats *dest, int flush) 656 { 657 struct btuart_softc *sc = device_private(self); 658 int s; 659 660 s = spltty(); 661 662 memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); 663 664 if (flush) 665 memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); 666 667 splx(s); 668 } 669