Home | History | Annotate | Line # | Download | only in btattach
btattach.c revision 1.10
      1 /*	$NetBSD: btattach.c,v 1.10 2010/03/08 21:48:42 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.  All rights reserved.");
     30 __RCSID("$NetBSD: btattach.c,v 1.10 2010/03/08 21:48:42 plunky 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 static void test(const char *, tcflag_t, tcflag_t);
     52 
     53 static int sigcount = 0;	/* signals received */
     54 static int opt_debug = 0;	/* global? */
     55 
     56 const struct devtype types[] = {
     57     {
     58 	.name = "bcm2035",
     59 	.line = "btuart",
     60 	.descr = "Broadcom BCM2035",
     61 	.init = &init_bcm2035,
     62 	.speed = B115200,
     63     },
     64     {
     65 	.name = "bcsp",
     66 	.line = "bcsp",
     67 	.descr = "Generic BlueCore Serial Protocol",
     68 	.cflag = CRTSCTS | PARENB,
     69 	.speed = B57600,
     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 = "Cambridge Silicon Radio based modules (not BCSP)",
     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 	.name = "unistone",
    140 	.line = "btuart",
    141 	.descr = "Infineon UniStone",
    142 	.init = &init_unistone,
    143 	.cflag = CRTSCTS,
    144 	.speed = B115200,
    145     },
    146 };
    147 
    148 int
    149 main(int argc, char *argv[])
    150 {
    151 	const struct devtype *type;
    152 	struct termios tio;
    153 	unsigned int init_speed, speed;
    154 	tcflag_t cflag, Cflag;
    155 	int fd, ch, tflag, i;
    156 	const char *name;
    157 	char *ptr;
    158 
    159 	init_speed = 0;
    160 	cflag = CLOCAL;
    161 	Cflag = 0;
    162 	tflag = 0;
    163 	name = "btuart";
    164 
    165 	while ((ch = getopt(argc, argv, "dFfi:oPpt")) != -1) {
    166 		switch (ch) {
    167 		case 'd':
    168 			opt_debug++;
    169 			break;
    170 
    171 		case 'F':
    172 			Cflag |= CRTSCTS;
    173 			break;
    174 
    175 		case 'f':
    176 			cflag |= CRTSCTS;
    177 			break;
    178 
    179 		case 'i':
    180 			init_speed = strtoul(optarg, &ptr, 10);
    181 			if (ptr[0] != '\0')
    182 				errx(EXIT_FAILURE, "invalid speed: %s", optarg);
    183 
    184 			break;
    185 
    186 		case 'o':
    187 			cflag |= (PARENB | PARODD);
    188 			break;
    189 
    190 		case 'P':
    191 			Cflag |= PARENB;
    192 			break;
    193 
    194 		case 'p':
    195 			cflag |= PARENB;
    196 			break;
    197 
    198 		case 't':
    199 			tflag = 1;
    200 			break;
    201 
    202 		case '?':
    203 		default:
    204 			usage();
    205 		}
    206 	}
    207 	argc -= optind;
    208 	argv += optind;
    209 
    210 	if (tflag) {
    211 		if (argc != 1)
    212 			usage();
    213 		test(argv[0], cflag, Cflag);
    214 		exit(EXIT_SUCCESS);
    215 	}
    216 
    217 	if (argc == 3) {
    218 		name = argv[0];
    219 		argv++;
    220 		argc--;
    221 	}
    222 
    223 	for (i = 0; ; i++) {
    224 		if (i == __arraycount(types))
    225 			errx(EXIT_FAILURE, "unknown type: %s", name);
    226 
    227 		type = &types[i];
    228 		if (strcasecmp(type->name, name) == 0)
    229 			break;
    230 	}
    231 
    232 	if (argc != 2)
    233 		usage();
    234 
    235 	/* parse tty speed */
    236 	speed = strtoul(argv[1], &ptr, 10);
    237 	if (ptr[0] != '\0')
    238 		errx(EXIT_FAILURE, "invalid speed: %s", argv[1]);
    239 
    240 	if (init_speed == 0)
    241 		init_speed = (type->speed ? type->speed : speed);
    242 
    243 	/* open tty */
    244 	if ((fd = open(argv[0], O_RDWR | O_EXLOCK, 0)) < 0)
    245 		err(EXIT_FAILURE, "%s", argv[0]);
    246 
    247 	/* setup tty */
    248 	if (tcgetattr(fd, &tio) < 0)
    249 		err(EXIT_FAILURE, "tcgetattr");
    250 
    251 	cfmakeraw(&tio);
    252 	tio.c_cflag |= (cflag | type->cflag);
    253 	tio.c_cflag &= ~Cflag;
    254 
    255 	if (cfsetspeed(&tio, init_speed) < 0
    256 	    || tcsetattr(fd, TCSANOW, &tio) < 0
    257 	    || tcflush(fd, TCIOFLUSH) < 0)
    258 		err(EXIT_FAILURE, "tty setup failed");
    259 
    260 	/* initialize device */
    261 	if (type->init != NULL)
    262 		(*type->init)(fd, speed);
    263 
    264 	if (cfsetspeed(&tio, speed) < 0
    265 	    || tcsetattr(fd, TCSADRAIN, &tio) < 0)
    266 		err(EXIT_FAILURE, "tty setup failed");
    267 
    268 	/* start line discipline */
    269 	if (ioctl(fd, TIOCSLINED, type->line) < 0)
    270 		err(EXIT_FAILURE, "%s", type->line);
    271 
    272 	if (opt_debug == 0 && daemon(0, 0) < 0)
    273 		warn("detach failed!");
    274 
    275 	/* store PID in "/var/run/btattach-{tty}.pid" */
    276 	ptr = strrchr(argv[0], '/');
    277 	asprintf(&ptr, "%s-%s", getprogname(), (ptr ? ptr + 1 : argv[0]));
    278 	if (ptr == NULL || pidfile(ptr) < 0)
    279 		warn("no pidfile");
    280 
    281 	free(ptr);
    282 
    283 	(void)signal(SIGHUP, sighandler);
    284 	(void)signal(SIGINT, sighandler);
    285 	(void)signal(SIGTERM, sighandler);
    286 	(void)signal(SIGTSTP, sighandler);
    287 	(void)signal(SIGUSR1, sighandler);
    288 	(void)signal(SIGUSR2, sighandler);
    289 
    290 	while (sigcount == 0)
    291 		select(0, NULL, NULL, NULL, NULL);
    292 
    293 	return EXIT_SUCCESS;
    294 }
    295 
    296 static void
    297 usage(void)
    298 {
    299 	size_t i;
    300 
    301 	fprintf(stderr,
    302 		"Usage: %s [-dFfoPp] [-i speed] [type] tty speed\n"
    303 		"       %s -t [-dFfoPp] tty\n"
    304 		"\n"
    305 		"Where:\n"
    306 		"\t-d          debug mode (no detach, dump io)\n"
    307 		"\t-F          disable flow control\n"
    308 		"\t-f          enable flow control\n"
    309 		"\t-i speed    init speed\n"
    310 		"\t-o          odd parity\n"
    311 		"\t-P          no parity\n"
    312 		"\t-p          even parity\n"
    313 		"\t-t          test mode\n"
    314 		"\n"
    315 		"Known types:\n"
    316 		"", getprogname(), getprogname());
    317 
    318 	for (i = 0; i < __arraycount(types); i++)
    319 		fprintf(stderr, "\t%-12s%s\n", types[i].name, types[i].descr);
    320 
    321 	exit(EXIT_FAILURE);
    322 }
    323 
    324 static void
    325 sighandler(int s)
    326 {
    327 
    328 	sigcount++;
    329 }
    330 
    331 static void
    332 hexdump(uint8_t *ptr, size_t len)
    333 {
    334 
    335 	while (len--)
    336 		printf(" %2.2x", *ptr++);
    337 }
    338 
    339 /*
    340  * send HCI comamnd
    341  */
    342 void
    343 uart_send_cmd(int fd, uint16_t opcode, void *buf, size_t len)
    344 {
    345 	struct iovec iov[2];
    346 	hci_cmd_hdr_t hdr;
    347 
    348 	hdr.type = HCI_CMD_PKT;
    349 	hdr.opcode = htole16(opcode);
    350 	hdr.length = len;
    351 
    352 	iov[0].iov_base = &hdr;
    353 	iov[0].iov_len = sizeof(hdr);
    354 	iov[1].iov_base = buf;
    355 	iov[1].iov_len = len;
    356 
    357 	if (opt_debug) {
    358 		printf("<<");
    359 		hexdump(iov[0].iov_base, iov[0].iov_len);
    360 		hexdump(iov[1].iov_base, iov[1].iov_len);
    361 		printf("\n");
    362 		fflush(stdout);
    363 	}
    364 
    365 	if (writev(fd, iov, __arraycount(iov)) < 0)
    366 		err(EXIT_FAILURE, "writev");
    367 
    368 	tcdrain(fd);
    369 }
    370 
    371 /*
    372  * get next character
    373  * store in iovec and inc counter if it fits
    374  */
    375 static uint8_t
    376 uart_getc(int fd, struct iovec *iov, int ioc, size_t *count)
    377 {
    378 	uint8_t ch, *b;
    379 	ssize_t n;
    380 	size_t off;
    381 
    382 	n = read(fd, &ch, sizeof(ch));
    383 	if (n < 0)
    384 		err(EXIT_FAILURE, "read");
    385 
    386 	if (n == 0)
    387 		errx(EXIT_FAILURE, "eof");
    388 
    389 	if (opt_debug)
    390 		printf(" %2.2x", ch);
    391 
    392 	off = *count;
    393 	while (ioc > 0) {
    394 		if (iov->iov_len > off) {
    395 			b = iov->iov_base;
    396 			b[off] = ch;
    397 			*count += 1;
    398 			break;
    399 		}
    400 
    401 		off -= iov->iov_len;
    402 		iov++;
    403 		ioc--;
    404 	}
    405 
    406 	return ch;
    407 }
    408 
    409 /*
    410  * read next packet, storing into iovec
    411  */
    412 static size_t
    413 uart_recv_pkt(int fd, struct iovec *iov, int ioc)
    414 {
    415 	size_t count, want;
    416 	uint8_t type;
    417 
    418 	if (opt_debug)
    419 		printf(">>");
    420 
    421 	count = 0;
    422 	type = uart_getc(fd, iov, ioc, &count);
    423 	switch(type) {
    424 	case HCI_EVENT_PKT:
    425 		(void)uart_getc(fd, iov, ioc, &count);	/* event */
    426 		want = uart_getc(fd, iov, ioc, &count);
    427 		break;
    428 
    429 	case HCI_ACL_DATA_PKT:
    430 		(void)uart_getc(fd, iov, ioc, &count);	/* handle LSB */
    431 		(void)uart_getc(fd, iov, ioc, &count);	/* handle MSB */
    432 		want = uart_getc(fd, iov, ioc, &count) |	/* LSB */
    433 		  uart_getc(fd, iov, ioc, &count) << 8;		/* MSB */
    434 		break;
    435 
    436 	case HCI_SCO_DATA_PKT:
    437 		(void)uart_getc(fd, iov, ioc, &count);	/* handle LSB */
    438 		(void)uart_getc(fd, iov, ioc, &count);	/* handle MSB */
    439 		want = uart_getc(fd, iov, ioc, &count);
    440 		break;
    441 
    442 	default: /* out of sync? */
    443 		errx(EXIT_FAILURE, "unknown packet type 0x%2.2x\n", type);
    444 	}
    445 
    446 	while (want-- > 0)
    447 		(void)uart_getc(fd, iov, ioc, &count);
    448 
    449 	if (opt_debug)
    450 		printf("\n");
    451 
    452 	return count;
    453 }
    454 
    455 /*
    456  * read next matching event packet to buffer
    457  */
    458 size_t
    459 uart_recv_ev(int fd, uint8_t event, void *buf, size_t len)
    460 {
    461 	struct iovec iov[2];
    462 	hci_event_hdr_t hdr;
    463 	size_t n;
    464 
    465 	iov[0].iov_base = &hdr;
    466 	iov[0].iov_len = sizeof(hdr);
    467 	iov[1].iov_base = buf;
    468 	iov[1].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 != event)
    475 			continue;
    476 
    477 		n -= sizeof(hdr);
    478 		break;
    479 	}
    480 
    481 	return n;
    482 }
    483 
    484 /*
    485  * read next matching command_complete event to buffer
    486  */
    487 size_t
    488 uart_recv_cc(int fd, uint16_t opcode, void *buf, size_t len)
    489 {
    490 	struct iovec iov[3];
    491 	hci_event_hdr_t hdr;
    492 	hci_command_compl_ep cc;
    493 	size_t n;
    494 
    495 	iov[0].iov_base = &hdr;
    496 	iov[0].iov_len = sizeof(hdr);
    497 	iov[1].iov_base = &cc;
    498 	iov[1].iov_len = sizeof(cc);
    499 	iov[2].iov_base = buf;
    500 	iov[2].iov_len = len;
    501 
    502 	for (;;) {
    503 		n = uart_recv_pkt(fd, iov, __arraycount(iov));
    504 		if (n < sizeof(hdr)
    505 		    || hdr.type != HCI_EVENT_PKT
    506 		    || hdr.event != HCI_EVENT_COMMAND_COMPL)
    507 			continue;
    508 
    509 		n -= sizeof(hdr);
    510 		if (n < sizeof(cc)
    511 		    || cc.opcode != htole16(opcode))
    512 			continue;
    513 
    514 		n -= sizeof(cc);
    515 		break;
    516 	}
    517 
    518 	return n;
    519 }
    520 
    521 static void
    522 test(const char *tty, tcflag_t cflag, tcflag_t Cflag)
    523 {
    524 	struct termios tio;
    525 	int fd, guessed;
    526 	size_t i, j, k;
    527 	ssize_t n;
    528 	unsigned char buf[32];
    529 	const int bauds[] = {
    530 		 57600,		/* BCSP specific default */
    531 		921600,		/* latest major baud rate */
    532 		115200,		/* old major baud rate */
    533 
    534 		460800,
    535 		230400,
    536 //		 76800,
    537 		 28800,
    538 		 38400,
    539 		 19200,
    540 		 14400,
    541 		  9600,
    542 		  7200,
    543 		  4800,
    544 		  2400,
    545 		  1800,
    546 		  1200,
    547 		   600,
    548 		   300,
    549 		   200,
    550 		   150,
    551 		   134,
    552 		   110,
    553 		    75,
    554 		    50,
    555 	};
    556 	const unsigned char bcsp_lepkt[] =
    557 	    /* ESC  ------- header -------  --- link establish ---   ESC */
    558 	    { 0xc0, 0x00, 0x41, 0x00, 0xbe, 0xda, 0xdc, 0xed, 0xed, 0xc0 };
    559 
    560 	printf("test mode\n");
    561 
    562 	/* open tty */
    563 	if ((fd = open(tty, O_RDWR | O_NONBLOCK | O_EXLOCK, 0)) < 0)
    564 		err(EXIT_FAILURE, "%s", tty);
    565 
    566 	/* setup tty */
    567 	if (tcgetattr(fd, &tio) < 0)
    568 		err(EXIT_FAILURE, "tcgetattr");
    569 	cfmakeraw(&tio);
    570 	tio.c_cflag |= (CLOCAL | CRTSCTS | PARENB);
    571 	tio.c_cflag |= cflag;
    572 	tio.c_cflag &= ~Cflag;
    573 
    574 	guessed = 0;
    575 	for (i = 0; i < __arraycount(bauds); i++) {
    576 		if (cfsetspeed(&tio, bauds[i]) < 0
    577 		    || tcsetattr(fd, TCSANOW, &tio) < 0
    578 		    || tcflush(fd, TCIOFLUSH) < 0) {
    579 			if (bauds[i] > 115200)
    580 				continue;
    581 			else
    582 				err(EXIT_FAILURE, "tty setup failed");
    583 		}
    584 
    585 		if (opt_debug)
    586 			printf("  try with B%d\n", bauds[i]);
    587 
    588 		sleep(bauds[i] < 9600 ? 3 : 1);
    589 
    590 		n = read(fd, buf, sizeof(buf));
    591 		if (opt_debug > 1)
    592 			printf("  %zd bytes read\n", n);
    593 		if (n < 0) {
    594 			if (i == 0 && errno == EAGAIN) {
    595 				printf("This module is *maybe* supported by btuart(4).\n"
    596 				    "you specify aproporiate <speed>.\n"
    597 				    "  Also can specify <type> for initialize.\n");
    598 				guessed = 1;
    599 				break;
    600 			}
    601 			if (errno == EAGAIN)
    602 				continue;
    603 
    604 			err(EXIT_FAILURE, "read");
    605 		} else {
    606 			if ((size_t)n < sizeof(bcsp_lepkt))
    607 				continue;
    608 			for (j = 0; j < n - sizeof(bcsp_lepkt); j++) {
    609 				for (k = 0; k < sizeof(bcsp_lepkt); k++)
    610 					if (buf[j + k] != bcsp_lepkt[k]) {
    611 						j += k;
    612 						break;
    613 					}
    614 				if (k < sizeof(bcsp_lepkt))
    615 					continue;
    616 
    617 				printf(
    618 				    "This module is supported by bcsp(4).\n"
    619 				    "  baud rate %d\n",
    620 				    bauds[i]);
    621 				if (tio.c_cflag & PARENB)
    622 					printf("  with %sparity\n",
    623 					    tio.c_cflag & PARODD ? "odd " : "");
    624 				guessed = 1;
    625 				break;
    626 			}
    627 			if (guessed)
    628 				break;
    629 		}
    630 
    631 	}
    632 
    633 	close(fd);
    634 
    635 	if (!guessed)
    636 		printf("don't understand...\n");
    637 }
    638