1 /* $NetBSD: btuart.c,v 1.24 2010/02/18 07:24:16 kiyohara 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.24 2010/02/18 07:24:16 kiyohara 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, "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, "out of memory\n"); 370 sc->sc_stats.err_rx++; 371 return 0; /* (lost sync) */ 372 } 373 374 m = m->m_next; 375 m->m_len = 0; 376 space = MLEN; 377 378 if (sc->sc_want > MINCLSIZE) { 379 MCLGET(m, M_DONTWAIT); 380 if (m->m_flags & M_EXT) 381 space = MCLBYTES; 382 } 383 } 384 } 385 386 mtod(m, uint8_t *)[m->m_len++] = c; 387 sc->sc_rxp->m_pkthdr.len++; 388 sc->sc_stats.byte_rx++; 389 390 sc->sc_want--; 391 if (sc->sc_want > 0) 392 return 0; /* want more */ 393 394 switch (sc->sc_state) { 395 case BTUART_RECV_PKT_TYPE: /* Got packet type */ 396 397 switch (c) { 398 case HCI_ACL_DATA_PKT: 399 sc->sc_state = BTUART_RECV_ACL_HDR; 400 sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; 401 break; 402 403 case HCI_SCO_DATA_PKT: 404 sc->sc_state = BTUART_RECV_SCO_HDR; 405 sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; 406 break; 407 408 case HCI_EVENT_PKT: 409 sc->sc_state = BTUART_RECV_EVENT_HDR; 410 sc->sc_want = sizeof(hci_event_hdr_t) - 1; 411 break; 412 413 default: 414 aprint_error_dev(sc->sc_dev, 415 "Unknown packet type=%#x!\n", c); 416 sc->sc_stats.err_rx++; 417 m_freem(sc->sc_rxp); 418 sc->sc_rxp = NULL; 419 return 0; /* (lost sync) */ 420 } 421 422 break; 423 424 /* 425 * we assume (correctly of course :) that the packet headers all fit 426 * into a single pkthdr mbuf 427 */ 428 case BTUART_RECV_ACL_HDR: /* Got ACL Header */ 429 sc->sc_state = BTUART_RECV_ACL_DATA; 430 sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; 431 sc->sc_want = le16toh(sc->sc_want); 432 break; 433 434 case BTUART_RECV_SCO_HDR: /* Got SCO Header */ 435 sc->sc_state = BTUART_RECV_SCO_DATA; 436 sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; 437 break; 438 439 case BTUART_RECV_EVENT_HDR: /* Got Event Header */ 440 sc->sc_state = BTUART_RECV_EVENT_DATA; 441 sc->sc_want = mtod(m, hci_event_hdr_t *)->length; 442 break; 443 444 case BTUART_RECV_ACL_DATA: /* ACL Packet Complete */ 445 if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) 446 sc->sc_stats.err_rx++; 447 448 sc->sc_stats.acl_rx++; 449 sc->sc_rxp = m = NULL; 450 break; 451 452 case BTUART_RECV_SCO_DATA: /* SCO Packet Complete */ 453 if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) 454 sc->sc_stats.err_rx++; 455 456 sc->sc_stats.sco_rx++; 457 sc->sc_rxp = m = NULL; 458 break; 459 460 case BTUART_RECV_EVENT_DATA: /* Event Packet Complete */ 461 if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) 462 sc->sc_stats.err_rx++; 463 464 sc->sc_stats.evt_rx++; 465 sc->sc_rxp = m = NULL; 466 break; 467 468 default: 469 panic("%s: invalid state %d!\n", 470 device_xname(sc->sc_dev), sc->sc_state); 471 } 472 473 return 0; 474 } 475 476 static int 477 btuartstart(struct tty *tp) 478 { 479 struct btuart_softc *sc = tp->t_sc; 480 struct mbuf *m; 481 int count, rlen; 482 uint8_t *rptr; 483 484 if (!sc->sc_enabled) 485 return 0; 486 487 m = sc->sc_txp; 488 if (m == NULL) { 489 if (MBUFQ_FIRST(&sc->sc_cmdq)) { 490 MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 491 sc->sc_stats.cmd_tx++; 492 } else if (MBUFQ_FIRST(&sc->sc_scoq)) { 493 MBUFQ_DEQUEUE(&sc->sc_scoq, m); 494 sc->sc_stats.sco_tx++; 495 } else if (MBUFQ_FIRST(&sc->sc_aclq)) { 496 MBUFQ_DEQUEUE(&sc->sc_aclq, m); 497 sc->sc_stats.acl_tx++; 498 } else { 499 sc->sc_xmit = false; 500 return 0; /* no more to send */ 501 } 502 503 sc->sc_txp = m; 504 sc->sc_xmit = true; 505 } 506 507 count = 0; 508 rlen = 0; 509 rptr = mtod(m, uint8_t *); 510 511 for(;;) { 512 if (rlen >= m->m_len) { 513 m = m->m_next; 514 if (m == NULL) { 515 m = sc->sc_txp; 516 sc->sc_txp = NULL; 517 518 if (M_GETCTX(m, void *) == NULL) 519 m_freem(m); 520 else if (!hci_complete_sco(sc->sc_unit, m)) 521 sc->sc_stats.err_tx++; 522 523 break; 524 } 525 526 rlen = 0; 527 rptr = mtod(m, uint8_t *); 528 continue; 529 } 530 531 if (putc(*rptr++, &tp->t_outq) < 0) { 532 m_adj(m, rlen); 533 break; 534 } 535 rlen++; 536 count++; 537 } 538 539 sc->sc_stats.byte_tx += count; 540 541 if (tp->t_outq.c_cc != 0) 542 (*tp->t_oproc)(tp); 543 544 return 0; 545 } 546 547 /***************************************************************************** 548 * 549 * bluetooth(9) functions 550 */ 551 552 static int 553 btuart_enable(device_t self) 554 { 555 struct btuart_softc *sc = device_private(self); 556 int s; 557 558 if (sc->sc_enabled) 559 return 0; 560 561 s = spltty(); 562 563 sc->sc_enabled = true; 564 sc->sc_xmit = false; 565 566 splx(s); 567 568 return 0; 569 } 570 571 static void 572 btuart_disable(device_t self) 573 { 574 struct btuart_softc *sc = device_private(self); 575 int s; 576 577 if (!sc->sc_enabled) 578 return; 579 580 s = spltty(); 581 582 if (sc->sc_rxp) { 583 m_freem(sc->sc_rxp); 584 sc->sc_rxp = NULL; 585 } 586 587 if (sc->sc_txp) { 588 m_freem(sc->sc_txp); 589 sc->sc_txp = NULL; 590 } 591 592 MBUFQ_DRAIN(&sc->sc_cmdq); 593 MBUFQ_DRAIN(&sc->sc_aclq); 594 MBUFQ_DRAIN(&sc->sc_scoq); 595 596 sc->sc_enabled = false; 597 598 splx(s); 599 } 600 601 static void 602 btuart_output_cmd(device_t self, struct mbuf *m) 603 { 604 struct btuart_softc *sc = device_private(self); 605 int s; 606 607 KASSERT(sc->sc_enabled); 608 609 M_SETCTX(m, NULL); 610 611 s = spltty(); 612 MBUFQ_ENQUEUE(&sc->sc_cmdq, m); 613 if (!sc->sc_xmit) 614 btuartstart(sc->sc_tp); 615 616 splx(s); 617 } 618 619 static void 620 btuart_output_acl(device_t self, struct mbuf *m) 621 { 622 struct btuart_softc *sc = device_private(self); 623 int s; 624 625 KASSERT(sc->sc_enabled); 626 627 M_SETCTX(m, NULL); 628 629 s = spltty(); 630 MBUFQ_ENQUEUE(&sc->sc_aclq, m); 631 if (!sc->sc_xmit) 632 btuartstart(sc->sc_tp); 633 634 splx(s); 635 } 636 637 static void 638 btuart_output_sco(device_t self, struct mbuf *m) 639 { 640 struct btuart_softc *sc = device_private(self); 641 int s; 642 643 KASSERT(sc->sc_enabled); 644 645 s = spltty(); 646 MBUFQ_ENQUEUE(&sc->sc_scoq, m); 647 if (!sc->sc_xmit) 648 btuartstart(sc->sc_tp); 649 650 splx(s); 651 } 652 653 static void 654 btuart_stats(device_t self, struct bt_stats *dest, int flush) 655 { 656 struct btuart_softc *sc = device_private(self); 657 int s; 658 659 s = spltty(); 660 661 memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); 662 663 if (flush) 664 memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); 665 666 splx(s); 667 } 668