1/* $NetBSD: btattach.c,v 1.16 2023/02/07 20:45:44 mlelstv Exp $ */ 2 3/*- 4 * Copyright (c) 2008 Iain Hibbert 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 WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert. All rights reserved."); 30__RCSID("$NetBSD: btattach.c,v 1.16 2023/02/07 20:45:44 mlelstv Exp $"); 31 32#include <sys/ioctl.h> 33#include <sys/param.h> 34#include <sys/uio.h> 35 36#include <bluetooth.h> 37#include <err.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <signal.h> 44#include <termios.h> 45#include <unistd.h> 46#include <util.h> 47 48#include "btattach.h" 49 50static void sighandler(int); 51__dead static void usage(void); 52static void test(const char *, tcflag_t, tcflag_t); 53 54static int sigcount = 0; /* signals received */ 55static int opt_debug = 0; /* global? */ 56 57static const struct devtype types[] = { 58 { 59 .name = "bcm2035", 60 .line = "btuart", 61 .descr = "Broadcom BCM2035", 62 .init = &init_bcm2035, 63 .speed = B115200, 64 }, 65 { 66 .name = "bcm43xx", 67 .line = "btuart", 68 .descr = "Broadcom BCM43xx", 69 .init = &init_bcm43xx, 70 .speed = B115200, 71 }, 72 { 73 .name = "bcm43xx-3wire", 74 .line = "bth5", 75 .descr = "Broadcom BCM43xx (3-wire)", 76 .init = &init_bcm43xx, 77 .speed = B115200, 78 }, 79 { 80 .name = "bcsp", 81 .line = "bcsp", 82 .descr = "Generic BlueCore Serial Protocol", 83 .cflag = CRTSCTS | PARENB, 84 .speed = B57600, 85 }, 86 { 87 .name = "bgb2xx", 88 .line = "btuart", 89 .descr = "Philips BGB2xx module", 90 .init = &init_bgb2xx, 91 .cflag = CRTSCTS, 92 .speed = B115200, 93 }, 94 { 95 .name = "btuart", 96 .line = "btuart", 97 .descr = "Generic UART (the default)", 98 }, 99 { 100 .name = "csr", 101 .line = "btuart", 102 .descr = "Cambridge Silicon Radio based modules (not BCSP)", 103 .init = &init_csr, 104 .cflag = CRTSCTS, 105 .speed = B57600, 106 }, 107 { 108 .name = "digi", 109 .line = "btuart", 110 .descr = "Digianswer based cards", 111 .init = &init_digi, 112 .cflag = CRTSCTS, 113 .speed = B9600, 114 }, 115 { 116 .name = "ericsson", 117 .line = "btuart", 118 .descr = "Ericsson based modules", 119 .init = &init_ericsson, 120 .cflag = CRTSCTS, 121 .speed = B57600, 122 }, 123 { 124 .name = "st", 125 .line = "btuart", 126 .descr = "ST Microelectronics minikits based on STLC2410/STLC2415", 127 .init = &init_st, 128 .cflag = CRTSCTS, 129 .speed = B57600, 130 }, 131 { 132 .name = "stlc2500", 133 .descr = "ST Microelectronics minikits based on STLC2500", 134 .init = &init_stlc2500, 135 .cflag = CRTSCTS, 136 .speed = B115200, 137 }, 138 { 139 .name = "swave", 140 .line = "btuart", 141 .descr = "Silicon Wave kits", 142 .init = &init_swave, 143 .cflag = CRTSCTS, 144 .speed = B57600, 145 }, 146 { 147 .name = "texas", 148 .line = "btuart", 149 .descr = "Texas Instruments", 150 .cflag = CRTSCTS, 151 .speed = B115200, 152 }, 153 { 154 .name = "unistone", 155 .line = "btuart", 156 .descr = "Infineon UniStone", 157 .init = &init_unistone, 158 .cflag = CRTSCTS, 159 .speed = B115200, 160 }, 161}; 162 163int 164main(int argc, char *argv[]) 165{ 166 const struct devtype *type; 167 struct termios tio; 168 unsigned int init_speed, speed; 169 tcflag_t cflag, Cflag; 170 int fd, ch, tflag, i; 171 const char *name; 172 char *ptr; 173 174 init_speed = 0; 175 cflag = CLOCAL; 176 Cflag = 0; 177 tflag = 0; 178 name = "btuart"; 179 180 while ((ch = getopt(argc, argv, "dFfi:oPpt")) != -1) { 181 switch (ch) { 182 case 'd': 183 opt_debug++; 184 break; 185 186 case 'F': 187 Cflag |= CRTSCTS; 188 break; 189 190 case 'f': 191 cflag |= CRTSCTS; 192 break; 193 194 case 'i': 195 init_speed = strtoul(optarg, &ptr, 10); 196 if (ptr[0] != '\0') 197 errx(EXIT_FAILURE, "invalid speed: %s", optarg); 198 199 break; 200 201 case 'o': 202 cflag |= (PARENB | PARODD); 203 break; 204 205 case 'P': 206 Cflag |= PARENB; 207 break; 208 209 case 'p': 210 cflag |= PARENB; 211 break; 212 213 case 't': 214 tflag = 1; 215 break; 216 217 case '?': 218 default: 219 usage(); 220 } 221 } 222 argc -= optind; 223 argv += optind; 224 225 if (tflag) { 226 if (argc != 1) 227 usage(); 228 test(argv[0], cflag, Cflag); 229 exit(EXIT_SUCCESS); 230 } 231 232 if (argc == 3) { 233 name = argv[0]; 234 argv++; 235 argc--; 236 } 237 238 for (i = 0; ; i++) { 239 if (i == __arraycount(types)) 240 errx(EXIT_FAILURE, "unknown type: %s", name); 241 242 type = &types[i]; 243 if (strcasecmp(type->name, name) == 0) 244 break; 245 } 246 247 if (argc != 2) 248 usage(); 249 250 /* parse tty speed */ 251 speed = strtoul(argv[1], &ptr, 10); 252 if (ptr[0] != '\0') 253 errx(EXIT_FAILURE, "invalid speed: %s", argv[1]); 254 255 if (init_speed == 0) 256 init_speed = (type->speed ? type->speed : speed); 257 258 /* open tty */ 259 if ((fd = open(argv[0], O_RDWR | O_EXLOCK, 0)) < 0) 260 err(EXIT_FAILURE, "%s", argv[0]); 261 262 /* setup tty */ 263 if (tcgetattr(fd, &tio) < 0) 264 err(EXIT_FAILURE, "tcgetattr"); 265 266 cfmakeraw(&tio); 267 tio.c_cflag |= (cflag | type->cflag); 268 tio.c_cflag &= ~Cflag; 269 270 if (cfsetspeed(&tio, init_speed) < 0 271 || tcsetattr(fd, TCSANOW, &tio) < 0 272 || tcflush(fd, TCIOFLUSH) < 0) 273 err(EXIT_FAILURE, "tty setup failed"); 274 275 /* initialize device */ 276 if (type->init != NULL) 277 (*type->init)(fd, speed); 278 279 if (speed != init_speed) { 280 if (cfsetspeed(&tio, speed) < 0 281 || tcsetattr(fd, TCSANOW, &tio) < 0) 282 err(EXIT_FAILURE, "tty setup failed"); 283 } 284 285 /* start line discipline */ 286 if (ioctl(fd, TIOCSLINED, type->line) < 0) 287 err(EXIT_FAILURE, "%s", type->line); 288 289 if (opt_debug == 0 && daemon(0, 0) < 0) 290 warn("detach failed!"); 291 292 /* store PID in "/var/run/btattach-{tty}.pid" */ 293 ptr = strrchr(argv[0], '/'); 294 asprintf(&ptr, "%s-%s", getprogname(), (ptr ? ptr + 1 : argv[0])); 295 if (ptr == NULL || pidfile(ptr) < 0) 296 warn("no pidfile"); 297 298 free(ptr); 299 300 (void)signal(SIGHUP, sighandler); 301 (void)signal(SIGINT, sighandler); 302 (void)signal(SIGTERM, sighandler); 303 (void)signal(SIGTSTP, sighandler); 304 (void)signal(SIGUSR1, sighandler); 305 (void)signal(SIGUSR2, sighandler); 306 307 while (sigcount == 0) 308 select(0, NULL, NULL, NULL, NULL); 309 310 return EXIT_SUCCESS; 311} 312 313static void 314usage(void) 315{ 316 size_t i; 317 318 fprintf(stderr, 319 "Usage: %s [-dFfoPp] [-i speed] [type] tty speed\n" 320 " %s -t [-dFfoPp] tty\n" 321 "\n" 322 "Where:\n" 323 "\t-d debug mode (no detach, dump io)\n" 324 "\t-F disable flow control\n" 325 "\t-f enable flow control\n" 326 "\t-i speed init speed\n" 327 "\t-o odd parity\n" 328 "\t-P no parity\n" 329 "\t-p even parity\n" 330 "\t-t test mode\n" 331 "\n" 332 "Known types:\n" 333 "", getprogname(), getprogname()); 334 335 for (i = 0; i < __arraycount(types); i++) 336 fprintf(stderr, "\t%-12s%s\n", types[i].name, types[i].descr); 337 338 exit(EXIT_FAILURE); 339} 340 341static void 342sighandler(int s) 343{ 344 345 sigcount++; 346} 347 348static void 349timeout(int s) 350{ 351 352} 353 354static void 355hexdump(uint8_t *ptr, size_t len) 356{ 357 358 while (len--) 359 printf(" %2.2x", *ptr++); 360} 361 362/* 363 * send HCI comamnd 364 */ 365int 366uart_send_cmd(int fd, uint16_t opcode, void *buf, size_t len) 367{ 368 struct iovec iov[2]; 369 hci_cmd_hdr_t hdr; 370 int r; 371 struct sigaction oaction, taction; 372 373 hdr.type = HCI_CMD_PKT; 374 hdr.opcode = htole16(opcode); 375 hdr.length = len; 376 377 iov[0].iov_base = &hdr; 378 iov[0].iov_len = sizeof(hdr); 379 iov[1].iov_base = buf; 380 iov[1].iov_len = len; 381 382 if (opt_debug) { 383 printf("<<"); 384 hexdump(iov[0].iov_base, iov[0].iov_len); 385 hexdump(iov[1].iov_base, iov[1].iov_len); 386 printf("\n"); 387 fflush(stdout); 388 } 389 390 if (writev(fd, iov, __arraycount(iov)) < 0) 391 err(EXIT_FAILURE, "writev"); 392 393 taction.sa_handler = timeout, 394 sigemptyset(&taction.sa_mask); 395 taction.sa_flags = 0, 396 397 sigaction(SIGALRM, &taction, &oaction); 398 alarm(1); 399 r = tcdrain(fd); 400 alarm(0); 401 sigaction(SIGALRM, &oaction, NULL); 402 403 return r; 404} 405 406/* 407 * get next character 408 * store in iovec and inc counter if it fits 409 */ 410static uint8_t 411uart_getc(int fd, struct iovec *iov, int ioc, size_t *count) 412{ 413 uint8_t ch, *b; 414 ssize_t n; 415 size_t off; 416 417 n = read(fd, &ch, sizeof(ch)); 418 if (n < 0) 419 err(EXIT_FAILURE, "read"); 420 421 if (n == 0) 422 errx(EXIT_FAILURE, "eof"); 423 424 if (opt_debug) 425 printf(" %2.2x", ch); 426 427 off = *count; 428 while (ioc > 0) { 429 if (iov->iov_len > off) { 430 b = iov->iov_base; 431 b[off] = ch; 432 *count += 1; 433 break; 434 } 435 436 off -= iov->iov_len; 437 iov++; 438 ioc--; 439 } 440 441 return ch; 442} 443 444/* 445 * read next packet, storing into iovec 446 */ 447static size_t 448uart_recv_pkt(int fd, struct iovec *iov, int ioc) 449{ 450 size_t count, want; 451 uint8_t type; 452 453 if (opt_debug) 454 printf(">>"); 455 456 count = 0; 457 type = uart_getc(fd, iov, ioc, &count); 458 switch(type) { 459 case HCI_EVENT_PKT: 460 (void)uart_getc(fd, iov, ioc, &count); /* event */ 461 want = uart_getc(fd, iov, ioc, &count); 462 break; 463 464 case HCI_ACL_DATA_PKT: 465 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */ 466 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */ 467 want = uart_getc(fd, iov, ioc, &count) | /* LSB */ 468 uart_getc(fd, iov, ioc, &count) << 8; /* MSB */ 469 break; 470 471 case HCI_SCO_DATA_PKT: 472 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */ 473 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */ 474 want = uart_getc(fd, iov, ioc, &count); 475 break; 476 477 default: /* out of sync? */ 478 errx(EXIT_FAILURE, "unknown packet type 0x%2.2x", type); 479 } 480 481 while (want-- > 0) 482 (void)uart_getc(fd, iov, ioc, &count); 483 484 if (opt_debug) 485 printf("\n"); 486 487 return count; 488} 489 490/* 491 * read next matching event packet to buffer 492 */ 493size_t 494uart_recv_ev(int fd, uint8_t event, void *buf, size_t len) 495{ 496 struct iovec iov[2]; 497 hci_event_hdr_t hdr; 498 size_t n; 499 500 iov[0].iov_base = &hdr; 501 iov[0].iov_len = sizeof(hdr); 502 iov[1].iov_base = buf; 503 iov[1].iov_len = len; 504 505 for (;;) { 506 n = uart_recv_pkt(fd, iov, __arraycount(iov)); 507 if (n < sizeof(hdr) 508 || hdr.type != HCI_EVENT_PKT 509 || hdr.event != event) 510 continue; 511 512 n -= sizeof(hdr); 513 break; 514 } 515 516 return n; 517} 518 519/* 520 * read next matching command_complete event to buffer 521 */ 522size_t 523uart_recv_cc(int fd, uint16_t opcode, void *buf, size_t len) 524{ 525 struct iovec iov[3]; 526 hci_event_hdr_t hdr; 527 hci_command_compl_ep cc; 528 size_t n; 529 530 iov[0].iov_base = &hdr; 531 iov[0].iov_len = sizeof(hdr); 532 iov[1].iov_base = &cc; 533 iov[1].iov_len = sizeof(cc); 534 iov[2].iov_base = buf; 535 iov[2].iov_len = len; 536 537 for (;;) { 538 n = uart_recv_pkt(fd, iov, __arraycount(iov)); 539 if (n < sizeof(hdr) 540 || hdr.type != HCI_EVENT_PKT 541 || hdr.event != HCI_EVENT_COMMAND_COMPL) 542 continue; 543 544 n -= sizeof(hdr); 545 if (n < sizeof(cc) 546 || cc.opcode != htole16(opcode)) 547 continue; 548 549 n -= sizeof(cc); 550 break; 551 } 552 553 return n; 554} 555 556static void 557test(const char *tty, tcflag_t cflag, tcflag_t Cflag) 558{ 559 struct termios tio; 560 int fd, guessed; 561 size_t i, j, k; 562 ssize_t n; 563 unsigned char buf[32]; 564 const int bauds[] = { 565 57600, /* BCSP specific default */ 566 921600, /* latest major baud rate */ 567 115200, /* old major baud rate */ 568 569 460800, 570 230400, 571// 76800, 572 28800, 573 38400, 574 19200, 575 14400, 576 9600, 577 7200, 578 4800, 579 2400, 580 1800, 581 1200, 582 600, 583 300, 584 200, 585 150, 586 134, 587 110, 588 75, 589 50, 590 }; 591 const unsigned char bcsp_lepkt[] = 592 /* ESC ------- header ------- --- link establish --- ESC */ 593 { 0xc0, 0x00, 0x41, 0x00, 0xbe, 0xda, 0xdc, 0xed, 0xed, 0xc0 }; 594 595 printf("test mode\n"); 596 597 /* open tty */ 598 if ((fd = open(tty, O_RDWR | O_NONBLOCK | O_EXLOCK, 0)) < 0) 599 err(EXIT_FAILURE, "%s", tty); 600 601 /* setup tty */ 602 if (tcgetattr(fd, &tio) < 0) 603 err(EXIT_FAILURE, "tcgetattr"); 604 cfmakeraw(&tio); 605 tio.c_cflag |= (CLOCAL | CRTSCTS | PARENB); 606 tio.c_cflag |= cflag; 607 tio.c_cflag &= ~Cflag; 608 609 guessed = 0; 610 for (i = 0; i < __arraycount(bauds); i++) { 611 if (cfsetspeed(&tio, bauds[i]) < 0 612 || tcsetattr(fd, TCSANOW, &tio) < 0 613 || tcflush(fd, TCIOFLUSH) < 0) { 614 if (bauds[i] > 115200) 615 continue; 616 else 617 err(EXIT_FAILURE, "tty setup failed"); 618 } 619 620 if (opt_debug) 621 printf(" try with B%d\n", bauds[i]); 622 623 sleep(bauds[i] < 9600 ? 3 : 1); 624 625 n = read(fd, buf, sizeof(buf)); 626 if (opt_debug > 1) 627 printf(" %zd bytes read\n", n); 628 if (n < 0) { 629 if (i == 0 && errno == EAGAIN) { 630 printf("This module is *maybe* supported by btuart(4).\n" 631 "you specify aproporiate <speed>.\n" 632 " Also can specify <type> for initialize.\n"); 633 guessed = 1; 634 break; 635 } 636 if (errno == EAGAIN) 637 continue; 638 639 err(EXIT_FAILURE, "read"); 640 } else { 641 if ((size_t)n < sizeof(bcsp_lepkt)) 642 continue; 643 for (j = 0; j < n - sizeof(bcsp_lepkt); j++) { 644 for (k = 0; k < sizeof(bcsp_lepkt); k++) 645 if (buf[j + k] != bcsp_lepkt[k]) { 646 j += k; 647 break; 648 } 649 if (k < sizeof(bcsp_lepkt)) 650 continue; 651 652 printf( 653 "This module is supported by bcsp(4).\n" 654 " baud rate %d\n", 655 bauds[i]); 656 if (tio.c_cflag & PARENB) 657 printf(" with %sparity\n", 658 tio.c_cflag & PARODD ? "odd " : ""); 659 guessed = 1; 660 break; 661 } 662 if (guessed) 663 break; 664 } 665 666 } 667 668 close(fd); 669 670 if (!guessed) 671 printf("don't understand...\n"); 672} 673