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