Home | History | Annotate | Line # | Download | only in btattach
      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 
     50 static void sighandler(int);
     51 __dead static void usage(void);
     52 static void test(const char *, tcflag_t, tcflag_t);
     53 
     54 static int sigcount = 0;	/* signals received */
     55 static int opt_debug = 0;	/* global? */
     56 
     57 static 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 
    163 int
    164 main(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 
    313 static void
    314 usage(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 
    341 static void
    342 sighandler(int s)
    343 {
    344 
    345 	sigcount++;
    346 }
    347 
    348 static void
    349 timeout(int s)
    350 {
    351 
    352 }
    353 
    354 static void
    355 hexdump(uint8_t *ptr, size_t len)
    356 {
    357 
    358 	while (len--)
    359 		printf(" %2.2x", *ptr++);
    360 }
    361 
    362 /*
    363  * send HCI comamnd
    364  */
    365 int
    366 uart_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  */
    410 static uint8_t
    411 uart_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  */
    447 static size_t
    448 uart_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  */
    493 size_t
    494 uart_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  */
    522 size_t
    523 uart_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 
    556 static void
    557 test(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