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