1 /* $NetBSD: btuart.c,v 1.29 2019/01/24 09:33:03 knakahara 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.29 2019/01/24 09:33:03 knakahara 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 static int btuart_match(device_t, cfdata_t, void *); 85 static void btuart_attach(device_t, device_t, void *); 86 static int btuart_detach(device_t, int); 87 88 static int btuartopen(dev_t, struct tty *); 89 static int btuartclose(struct tty *, int); 90 static int btuartioctl(struct tty *, u_long, void *, int, struct lwp *); 91 static int btuartinput(int, struct tty *); 92 static int btuartstart(struct tty *); 93 94 static int btuart_enable(device_t); 95 static void btuart_disable(device_t); 96 static void btuart_output_cmd(device_t, struct mbuf *); 97 static void btuart_output_acl(device_t, struct mbuf *); 98 static void btuart_output_sco(device_t, struct mbuf *); 99 static void btuart_stats(device_t, struct bt_stats *, int); 100 101 /* 102 * It doesn't need to be exported, as only btuartattach() uses it, 103 * but there's no "official" way to make it static. 104 */ 105 CFATTACH_DECL_NEW(btuart, sizeof(struct btuart_softc), 106 btuart_match, btuart_attach, btuart_detach, NULL); 107 108 static struct linesw btuart_disc = { 109 .l_name = "btuart", 110 .l_open = btuartopen, 111 .l_close = btuartclose, 112 .l_read = ttyerrio, 113 .l_write = ttyerrio, 114 .l_ioctl = btuartioctl, 115 .l_rint = btuartinput, 116 .l_start = btuartstart, 117 .l_modem = ttymodem, 118 .l_poll = ttyerrpoll, 119 }; 120 121 static const struct hci_if btuart_hci = { 122 .enable = btuart_enable, 123 .disable = btuart_disable, 124 .output_cmd = btuart_output_cmd, 125 .output_acl = btuart_output_acl, 126 .output_sco = btuart_output_sco, 127 .get_stats = btuart_stats, 128 .ipl = IPL_TTY, 129 }; 130 131 /***************************************************************************** 132 * 133 * autoconf(9) functions 134 */ 135 136 /* 137 * pseudo-device attach routine. 138 */ 139 void 140 btuartattach(int num __unused) 141 { 142 int error; 143 144 error = ttyldisc_attach(&btuart_disc); 145 if (error) { 146 aprint_error("%s: unable to register line discipline, " 147 "error = %d\n", btuart_cd.cd_name, error); 148 149 return; 150 } 151 152 error = config_cfattach_attach(btuart_cd.cd_name, &btuart_ca); 153 if (error) { 154 aprint_error("%s: unable to register cfattach, error = %d\n", 155 btuart_cd.cd_name, error); 156 157 config_cfdriver_detach(&btuart_cd); 158 (void) ttyldisc_detach(&btuart_disc); 159 } 160 } 161 162 /* 163 * Autoconf match routine. 164 */ 165 static int 166 btuart_match(device_t self __unused, cfdata_t cfdata __unused, 167 void *arg __unused) 168 { 169 170 /* pseudo-device; always present */ 171 return 1; 172 } 173 174 /* 175 * Autoconf attach routine. 176 * Called by config_attach_pseudo(9) when we open the line discipline. 177 */ 178 static void 179 btuart_attach(device_t parent __unused, device_t self, void *aux __unused) 180 { 181 struct btuart_softc *sc = device_private(self); 182 183 sc->sc_dev = self; 184 185 MBUFQ_INIT(&sc->sc_cmdq); 186 MBUFQ_INIT(&sc->sc_aclq); 187 MBUFQ_INIT(&sc->sc_scoq); 188 189 /* Attach Bluetooth unit */ 190 sc->sc_unit = hci_attach_pcb(&btuart_hci, self, 0); 191 if (sc->sc_unit == NULL) 192 aprint_error_dev(self, "HCI attach failed\n"); 193 } 194 195 /* 196 * Autoconf detach routine. 197 * Called when we close the line discipline. 198 */ 199 static int 200 btuart_detach(device_t self, int flags __unused) 201 { 202 struct btuart_softc *sc = device_private(self); 203 204 btuart_disable(self); 205 206 if (sc->sc_unit) { 207 hci_detach_pcb(sc->sc_unit); 208 sc->sc_unit = NULL; 209 } 210 211 return 0; 212 } 213 214 /***************************************************************************** 215 * 216 * Line discipline functions. 217 */ 218 219 static int 220 btuartopen(dev_t devno __unused, struct tty *tp) 221 { 222 struct btuart_softc *sc; 223 device_t dev; 224 cfdata_t cfdata; 225 struct lwp *l = curlwp; /* XXX */ 226 int error, unit, s; 227 228 error = kauth_authorize_device(l->l_cred, KAUTH_DEVICE_BLUETOOTH_BTUART, 229 KAUTH_ARG(KAUTH_REQ_DEVICE_BLUETOOTH_BTUART_ADD), NULL, NULL, NULL); 230 if (error) 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 cfdata_t 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 /* 317 * XXX 318 * This function can be called without KERNEL_LOCK when caller's 319 * struct cdevsw is set D_MPSAFE. Is KERNEL_LOCK required? 320 */ 321 322 if (sc == NULL || tp != sc->sc_tp) 323 return EPASSTHROUGH; 324 325 switch(cmd) { 326 default: 327 error = EPASSTHROUGH; 328 break; 329 } 330 331 return error; 332 } 333 334 static int 335 btuartinput(int c, struct tty *tp) 336 { 337 struct btuart_softc *sc = tp->t_sc; 338 struct mbuf *m = sc->sc_rxp; 339 int space = 0; 340 341 if (!sc->sc_enabled) 342 return 0; 343 344 c &= TTY_CHARMASK; 345 346 /* If we already started a packet, find the trailing end of it. */ 347 if (m) { 348 while (m->m_next) 349 m = m->m_next; 350 351 space = M_TRAILINGSPACE(m); 352 } 353 354 if (space == 0) { 355 if (m == NULL) { 356 /* new packet */ 357 MGETHDR(m, M_DONTWAIT, MT_DATA); 358 if (m == NULL) { 359 aprint_error_dev(sc->sc_dev, "out of memory\n"); 360 sc->sc_stats.err_rx++; 361 return 0; /* (lost sync) */ 362 } 363 364 sc->sc_rxp = m; 365 m->m_pkthdr.len = m->m_len = 0; 366 space = MHLEN; 367 368 sc->sc_state = BTUART_RECV_PKT_TYPE; 369 sc->sc_want = 1; 370 } else { 371 /* extend mbuf */ 372 MGET(m->m_next, M_DONTWAIT, MT_DATA); 373 if (m->m_next == NULL) { 374 aprint_error_dev(sc->sc_dev, "out of memory\n"); 375 sc->sc_stats.err_rx++; 376 return 0; /* (lost sync) */ 377 } 378 379 m = m->m_next; 380 m->m_len = 0; 381 space = MLEN; 382 383 if (sc->sc_want > MINCLSIZE) { 384 MCLGET(m, M_DONTWAIT); 385 if (m->m_flags & M_EXT) 386 space = MCLBYTES; 387 } 388 } 389 } 390 391 mtod(m, uint8_t *)[m->m_len++] = c; 392 sc->sc_rxp->m_pkthdr.len++; 393 sc->sc_stats.byte_rx++; 394 395 sc->sc_want--; 396 if (sc->sc_want > 0) 397 return 0; /* want more */ 398 399 switch (sc->sc_state) { 400 case BTUART_RECV_PKT_TYPE: /* Got packet type */ 401 402 switch (c) { 403 case HCI_ACL_DATA_PKT: 404 sc->sc_state = BTUART_RECV_ACL_HDR; 405 sc->sc_want = sizeof(hci_acldata_hdr_t) - 1; 406 break; 407 408 case HCI_SCO_DATA_PKT: 409 sc->sc_state = BTUART_RECV_SCO_HDR; 410 sc->sc_want = sizeof(hci_scodata_hdr_t) - 1; 411 break; 412 413 case HCI_EVENT_PKT: 414 sc->sc_state = BTUART_RECV_EVENT_HDR; 415 sc->sc_want = sizeof(hci_event_hdr_t) - 1; 416 break; 417 418 default: 419 aprint_error_dev(sc->sc_dev, 420 "Unknown packet type=%#x!\n", c); 421 sc->sc_stats.err_rx++; 422 m_freem(sc->sc_rxp); 423 sc->sc_rxp = NULL; 424 return 0; /* (lost sync) */ 425 } 426 427 break; 428 429 /* 430 * we assume (correctly of course :) that the packet headers all fit 431 * into a single pkthdr mbuf 432 */ 433 case BTUART_RECV_ACL_HDR: /* Got ACL Header */ 434 sc->sc_state = BTUART_RECV_ACL_DATA; 435 sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length; 436 sc->sc_want = le16toh(sc->sc_want); 437 break; 438 439 case BTUART_RECV_SCO_HDR: /* Got SCO Header */ 440 sc->sc_state = BTUART_RECV_SCO_DATA; 441 sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length; 442 break; 443 444 case BTUART_RECV_EVENT_HDR: /* Got Event Header */ 445 sc->sc_state = BTUART_RECV_EVENT_DATA; 446 sc->sc_want = mtod(m, hci_event_hdr_t *)->length; 447 break; 448 449 case BTUART_RECV_ACL_DATA: /* ACL Packet Complete */ 450 if (!hci_input_acl(sc->sc_unit, sc->sc_rxp)) 451 sc->sc_stats.err_rx++; 452 453 sc->sc_stats.acl_rx++; 454 sc->sc_rxp = m = NULL; 455 break; 456 457 case BTUART_RECV_SCO_DATA: /* SCO Packet Complete */ 458 if (!hci_input_sco(sc->sc_unit, sc->sc_rxp)) 459 sc->sc_stats.err_rx++; 460 461 sc->sc_stats.sco_rx++; 462 sc->sc_rxp = m = NULL; 463 break; 464 465 case BTUART_RECV_EVENT_DATA: /* Event Packet Complete */ 466 if (!hci_input_event(sc->sc_unit, sc->sc_rxp)) 467 sc->sc_stats.err_rx++; 468 469 sc->sc_stats.evt_rx++; 470 sc->sc_rxp = m = NULL; 471 break; 472 473 default: 474 panic("%s: invalid state %d!\n", 475 device_xname(sc->sc_dev), sc->sc_state); 476 } 477 478 return 0; 479 } 480 481 static int 482 btuartstart(struct tty *tp) 483 { 484 struct btuart_softc *sc = tp->t_sc; 485 struct mbuf *m; 486 int count, rlen; 487 uint8_t *rptr; 488 489 if (!sc->sc_enabled) 490 return 0; 491 492 m = sc->sc_txp; 493 if (m == NULL) { 494 if (MBUFQ_FIRST(&sc->sc_cmdq)) { 495 MBUFQ_DEQUEUE(&sc->sc_cmdq, m); 496 sc->sc_stats.cmd_tx++; 497 } else if (MBUFQ_FIRST(&sc->sc_scoq)) { 498 MBUFQ_DEQUEUE(&sc->sc_scoq, m); 499 sc->sc_stats.sco_tx++; 500 } else if (MBUFQ_FIRST(&sc->sc_aclq)) { 501 MBUFQ_DEQUEUE(&sc->sc_aclq, m); 502 sc->sc_stats.acl_tx++; 503 } else { 504 sc->sc_xmit = false; 505 return 0; /* no more to send */ 506 } 507 508 sc->sc_txp = m; 509 sc->sc_xmit = true; 510 } 511 512 count = 0; 513 rlen = 0; 514 rptr = mtod(m, uint8_t *); 515 516 for(;;) { 517 if (rlen >= m->m_len) { 518 m = m->m_next; 519 if (m == NULL) { 520 m = sc->sc_txp; 521 sc->sc_txp = NULL; 522 523 if (M_GETCTX(m, void *) == NULL) 524 m_freem(m); 525 else if (!hci_complete_sco(sc->sc_unit, m)) 526 sc->sc_stats.err_tx++; 527 528 break; 529 } 530 531 rlen = 0; 532 rptr = mtod(m, uint8_t *); 533 continue; 534 } 535 536 if (putc(*rptr++, &tp->t_outq) < 0) { 537 m_adj(m, rlen); 538 break; 539 } 540 rlen++; 541 count++; 542 } 543 544 sc->sc_stats.byte_tx += count; 545 546 if (tp->t_outq.c_cc != 0) 547 (*tp->t_oproc)(tp); 548 549 return 0; 550 } 551 552 /***************************************************************************** 553 * 554 * bluetooth(9) functions 555 */ 556 557 static int 558 btuart_enable(device_t self) 559 { 560 struct btuart_softc *sc = device_private(self); 561 int s; 562 563 if (sc->sc_enabled) 564 return 0; 565 566 s = spltty(); 567 568 sc->sc_enabled = true; 569 sc->sc_xmit = false; 570 571 splx(s); 572 573 return 0; 574 } 575 576 static void 577 btuart_disable(device_t self) 578 { 579 struct btuart_softc *sc = device_private(self); 580 int s; 581 582 if (!sc->sc_enabled) 583 return; 584 585 s = spltty(); 586 587 if (sc->sc_rxp) { 588 m_freem(sc->sc_rxp); 589 sc->sc_rxp = NULL; 590 } 591 592 if (sc->sc_txp) { 593 m_freem(sc->sc_txp); 594 sc->sc_txp = NULL; 595 } 596 597 MBUFQ_DRAIN(&sc->sc_cmdq); 598 MBUFQ_DRAIN(&sc->sc_aclq); 599 MBUFQ_DRAIN(&sc->sc_scoq); 600 601 sc->sc_enabled = false; 602 603 splx(s); 604 } 605 606 static void 607 btuart_output_cmd(device_t self, struct mbuf *m) 608 { 609 struct btuart_softc *sc = device_private(self); 610 int s; 611 612 KASSERT(sc->sc_enabled); 613 614 M_SETCTX(m, NULL); 615 616 s = spltty(); 617 MBUFQ_ENQUEUE(&sc->sc_cmdq, m); 618 if (!sc->sc_xmit) 619 btuartstart(sc->sc_tp); 620 621 splx(s); 622 } 623 624 static void 625 btuart_output_acl(device_t self, struct mbuf *m) 626 { 627 struct btuart_softc *sc = device_private(self); 628 int s; 629 630 KASSERT(sc->sc_enabled); 631 632 M_SETCTX(m, NULL); 633 634 s = spltty(); 635 MBUFQ_ENQUEUE(&sc->sc_aclq, m); 636 if (!sc->sc_xmit) 637 btuartstart(sc->sc_tp); 638 639 splx(s); 640 } 641 642 static void 643 btuart_output_sco(device_t self, struct mbuf *m) 644 { 645 struct btuart_softc *sc = device_private(self); 646 int s; 647 648 KASSERT(sc->sc_enabled); 649 650 s = spltty(); 651 MBUFQ_ENQUEUE(&sc->sc_scoq, m); 652 if (!sc->sc_xmit) 653 btuartstart(sc->sc_tp); 654 655 splx(s); 656 } 657 658 static void 659 btuart_stats(device_t self, struct bt_stats *dest, int flush) 660 { 661 struct btuart_softc *sc = device_private(self); 662 int s; 663 664 s = spltty(); 665 666 memcpy(dest, &sc->sc_stats, sizeof(struct bt_stats)); 667 668 if (flush) 669 memset(&sc->sc_stats, 0, sizeof(struct bt_stats)); 670 671 splx(s); 672 } 673