1 /* $NetBSD: btattach.c,v 1.2 2008/07/21 13:36:57 lukem 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.2 2008/07/21 13:36:57 lukem 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 <termios.h> 44 #include <unistd.h> 45 #include <util.h> 46 47 #include "btattach.h" 48 49 static void sighandler(int); 50 static void usage(void); 51 52 static int sigcount = 0; /* signals received */ 53 static int opt_debug = 0; /* global? */ 54 55 const struct devtype types[] = { 56 { 57 .name = "bcsp", 58 .line = "bcsp", 59 .descr = "Generic BlueCore Serial Protocol", 60 .cflag = CRTSCTS, 61 .speed = B57600, 62 }, 63 { 64 .name = "bcm2035", 65 .line = "btuart", 66 .descr = "Broadcom BCM2035", 67 .init = &init_bcm2035, 68 .speed = B115200, 69 }, 70 { 71 .name = "bgb2xx", 72 .line = "btuart", 73 .descr = "Philips BGB2xx module", 74 .init = &init_bgb2xx, 75 .cflag = CRTSCTS, 76 .speed = B115200, 77 }, 78 { 79 .name = "btuart", 80 .line = "btuart", 81 .descr = "Generic UART (the default)", 82 }, 83 { 84 .name = "csr", 85 .line = "btuart", 86 .descr = "CSR Casira serial adaptor", 87 .init = &init_csr, 88 .cflag = CRTSCTS, 89 .speed = B57600, 90 }, 91 { 92 .name = "digi", 93 .line = "btuart", 94 .descr = "Digianswer based cards", 95 .init = &init_digi, 96 .cflag = CRTSCTS, 97 .speed = B9600, 98 }, 99 { 100 .name = "ericsson", 101 .line = "btuart", 102 .descr = "Ericsson based modules", 103 .init = &init_ericsson, 104 .cflag = CRTSCTS, 105 .speed = B57600, 106 }, 107 { 108 .name = "st", 109 .line = "btuart", 110 .descr = "ST Microelectronics minikits based on STLC2410/STLC2415", 111 .init = &init_st, 112 .cflag = CRTSCTS, 113 .speed = B57600, 114 }, 115 { 116 .name = "stlc2500", 117 .descr = "ST Microelectronics minikits based on STLC2500", 118 .init = &init_stlc2500, 119 .cflag = CRTSCTS, 120 .speed = B115200, 121 }, 122 { 123 .name = "swave", 124 .line = "btuart", 125 .descr = "Silicon Wave kits", 126 .init = &init_swave, 127 .cflag = CRTSCTS, 128 .speed = B57600, 129 }, 130 { 131 .name = "texas", 132 .line = "btuart", 133 .descr = "Texas Instruments", 134 .cflag = CRTSCTS, 135 .speed = B115200, 136 }, 137 }; 138 139 int 140 main(int argc, char *argv[]) 141 { 142 const struct devtype *type; 143 struct termios tio; 144 unsigned int init_speed, speed; 145 tcflag_t cflag; 146 int fd, ch, i; 147 const char *name; 148 char *ptr; 149 150 init_speed = 0; 151 cflag = CLOCAL; 152 name = "btuart"; 153 154 while ((ch = getopt(argc, argv, "dfi:op")) != -1) { 155 switch (ch) { 156 case 'd': 157 opt_debug++; 158 break; 159 160 case 'f': 161 cflag |= CRTSCTS; 162 break; 163 164 case 'i': 165 init_speed = strtoul(optarg, &ptr, 10); 166 if (ptr[0] != '\0') 167 errx(EXIT_FAILURE, "invalid speed: %s", optarg); 168 169 break; 170 171 case 'o': 172 cflag |= (PARENB | PARODD); 173 break; 174 175 case 'p': 176 cflag |= PARENB; 177 break; 178 179 case '?': 180 default: 181 usage(); 182 } 183 } 184 argc -= optind; 185 argv += optind; 186 187 if (argc == 3) { 188 name = argv[0]; 189 argv++; 190 argc--; 191 } 192 193 for (i = 0; ; i++) { 194 if (i == __arraycount(types)) 195 errx(EXIT_FAILURE, "unknown type: %s", name); 196 197 type = &types[i]; 198 if (strcasecmp(type->name, name) == 0) 199 break; 200 } 201 202 if (argc != 2) 203 usage(); 204 205 /* parse tty speed */ 206 speed = strtoul(argv[1], &ptr, 10); 207 if (ptr[0] != '\0') 208 errx(EXIT_FAILURE, "invalid speed: %s", argv[1]); 209 210 if (init_speed == 0) 211 init_speed = (type->speed ? type->speed : speed); 212 213 /* open tty */ 214 if ((fd = open(argv[0], O_RDWR | O_NDELAY | O_EXLOCK, 0)) < 0) 215 err(EXIT_FAILURE, "%s", argv[0]); 216 217 /* setup tty */ 218 if (tcgetattr(fd, &tio) < 0) 219 err(EXIT_FAILURE, "tcgetattr"); 220 221 cfmakeraw(&tio); 222 tio.c_cflag |= (cflag | type->cflag); 223 224 if (cfsetspeed(&tio, init_speed) < 0 225 || tcsetattr(fd, TCSANOW, &tio) < 0 226 || tcflush(fd, TCIOFLUSH) < 0) 227 err(EXIT_FAILURE, "tty setup failed"); 228 229 /* initialize device */ 230 if (type->init != NULL) 231 (*type->init)(fd, speed); 232 233 if (cfsetspeed(&tio, speed) < 0 234 || tcsetattr(fd, TCSADRAIN, &tio) < 0) 235 err(EXIT_FAILURE, "tty setup failed"); 236 237 /* start line discipline */ 238 if (ioctl(fd, TIOCSLINED, type->line) < 0) 239 err(EXIT_FAILURE, "%s", type->line); 240 241 if (opt_debug == 0 && daemon(0, 0) < 0) 242 warn("detach failed!"); 243 244 /* store PID in "/var/run/btattach-{tty}.pid" */ 245 ptr = strrchr(argv[0], '/'); 246 asprintf(&ptr, "%s-%s", getprogname(), (ptr ? ptr + 1 : argv[0])); 247 if (ptr == NULL || pidfile(ptr) < 0) 248 warn("no pidfile"); 249 250 free(ptr); 251 252 (void)signal(SIGHUP, sighandler); 253 (void)signal(SIGINT, sighandler); 254 (void)signal(SIGTERM, sighandler); 255 (void)signal(SIGTSTP, sighandler); 256 (void)signal(SIGUSR1, sighandler); 257 (void)signal(SIGUSR2, sighandler); 258 259 while (sigcount == 0) 260 select(0, NULL, NULL, NULL, NULL); 261 262 return EXIT_SUCCESS; 263 } 264 265 static void 266 usage(void) 267 { 268 int i; 269 270 fprintf(stderr, 271 "Usage: %s [-dfop] [-i speed] [type] tty speed\n" 272 "\n" 273 "Where:\n" 274 "\t-d debug mode (no detach, dump io)\n" 275 "\t-f enable flow control\n" 276 "\t-i speed init speed\n" 277 "\t-o odd parity\n" 278 "\t-p even parity\n" 279 "\n" 280 "Known types:\n" 281 "", getprogname()); 282 283 for (i = 0; i < __arraycount(types); i++) 284 fprintf(stderr, "\t%-12s%s\n", types[i].name, types[i].descr); 285 286 exit(EXIT_FAILURE); 287 } 288 289 static void 290 sighandler(int s) 291 { 292 293 sigcount++; 294 } 295 296 static void 297 hexdump(uint8_t *ptr, size_t len) 298 { 299 300 while (len--) 301 printf(" %2.2x", *ptr++); 302 } 303 304 /* 305 * send HCI comamnd 306 */ 307 void 308 uart_send_cmd(int fd, uint16_t opcode, void *buf, size_t len) 309 { 310 struct iovec iov[2]; 311 hci_cmd_hdr_t hdr; 312 313 hdr.type = HCI_CMD_PKT; 314 hdr.opcode = htole16(opcode); 315 hdr.length = len; 316 317 iov[0].iov_base = &hdr; 318 iov[0].iov_len = sizeof(hdr); 319 iov[1].iov_base = buf; 320 iov[1].iov_len = len; 321 322 if (opt_debug) { 323 printf("<<"); 324 hexdump(iov[0].iov_base, iov[0].iov_len); 325 hexdump(iov[1].iov_base, iov[1].iov_len); 326 printf("\n"); 327 fflush(stdout); 328 } 329 330 if (writev(fd, iov, __arraycount(iov)) < 0) 331 err(EXIT_FAILURE, "writev"); 332 333 tcdrain(fd); 334 } 335 336 /* 337 * get next character 338 * store in iovec and inc counter if it fits 339 */ 340 static uint8_t 341 uart_getc(int fd, struct iovec *iov, int ioc, size_t *count) 342 { 343 uint8_t ch, *b; 344 ssize_t n; 345 size_t off; 346 347 n = read(fd, &ch, sizeof(ch)); 348 if (n < 0) 349 err(EXIT_FAILURE, "read"); 350 351 if (n == 0) 352 errx(EXIT_FAILURE, "eof"); 353 354 if (opt_debug) 355 printf(" %2.2x", ch); 356 357 off = *count; 358 while (ioc > 0) { 359 if (iov->iov_len > off) { 360 b = iov->iov_base; 361 b[off] = ch; 362 *count += 1; 363 break; 364 } 365 366 off -= iov->iov_len; 367 iov++; 368 ioc--; 369 } 370 371 return ch; 372 } 373 374 /* 375 * read next packet, storing into iovec 376 */ 377 static size_t 378 uart_recv_pkt(int fd, struct iovec *iov, int ioc) 379 { 380 size_t count, want; 381 uint8_t type; 382 383 if (opt_debug) 384 printf(">>"); 385 386 count = 0; 387 type = uart_getc(fd, iov, ioc, &count); 388 switch(type) { 389 case HCI_EVENT_PKT: 390 (void)uart_getc(fd, iov, ioc, &count); /* event */ 391 want = sizeof(hci_event_hdr_t); 392 want += uart_getc(fd, iov, ioc, &count); 393 break; 394 395 case HCI_ACL_DATA_PKT: 396 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */ 397 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */ 398 want = sizeof(hci_acldata_hdr_t); 399 want += uart_getc(fd, iov, ioc, &count); /* LSB */ 400 want += uart_getc(fd, iov, ioc, &count) << 8; /* MSB */ 401 break; 402 403 case HCI_SCO_DATA_PKT: 404 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */ 405 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */ 406 want = sizeof(hci_scodata_hdr_t); 407 want += uart_getc(fd, iov, ioc, &count); 408 break; 409 410 default: /* out of sync? */ 411 err(EXIT_FAILURE, "unknown packet type 0x%2.2x", type); 412 } 413 414 while (want-- > 0) 415 (void)uart_getc(fd, iov, ioc, &count); 416 417 if (opt_debug) 418 printf("\n"); 419 420 return count; 421 } 422 423 /* 424 * read next matching event packet to buffer 425 */ 426 size_t 427 uart_recv_ev(int fd, uint8_t event, void *buf, size_t len) 428 { 429 struct iovec iov[2]; 430 hci_event_hdr_t hdr; 431 size_t n; 432 433 iov[0].iov_base = &hdr; 434 iov[0].iov_len = sizeof(hdr); 435 iov[1].iov_base = buf; 436 iov[1].iov_len = len; 437 438 for (;;) { 439 n = uart_recv_pkt(fd, iov, __arraycount(iov)); 440 if (n < sizeof(hdr) 441 || hdr.type != HCI_EVENT_PKT 442 || hdr.event != event) 443 continue; 444 445 n -= sizeof(hdr); 446 break; 447 } 448 449 return n; 450 } 451 452 /* 453 * read next matching command_complete event to buffer 454 */ 455 size_t 456 uart_recv_cc(int fd, uint16_t opcode, void *buf, size_t len) 457 { 458 struct iovec iov[3]; 459 hci_event_hdr_t hdr; 460 hci_command_compl_ep cc; 461 size_t n; 462 463 iov[0].iov_base = &hdr; 464 iov[0].iov_len = sizeof(hdr); 465 iov[1].iov_base = &cc; 466 iov[1].iov_len = sizeof(cc); 467 iov[2].iov_base = buf; 468 iov[2].iov_len = len; 469 470 for (;;) { 471 n = uart_recv_pkt(fd, iov, __arraycount(iov)); 472 if (n < sizeof(hdr) 473 || hdr.type != HCI_EVENT_PKT 474 || hdr.event != HCI_EVENT_COMMAND_COMPL) 475 continue; 476 477 n -= sizeof(hdr); 478 if (n < sizeof(cc) 479 || cc.opcode != htole16(opcode)) 480 continue; 481 482 n -= sizeof(cc); 483 break; 484 } 485 486 return n; 487 } 488