Home | History | Annotate | Line # | Download | only in btattach
btattach.c revision 1.1
      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