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