Home | History | Annotate | Line # | Download | only in btattach
btattach.c revision 1.3
      1 /*	$NetBSD: btattach.c,v 1.3 2009/04/15 00:32:23 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.3 2009/04/15 00:32:23 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 	size_t 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