xref: /netbsd/usr.sbin/btattach/btattach.c (revision c1a1d950)
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
main(int argc,char * argv[])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
usage(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
sighandler(int s)342 sighandler(int s)
343 {
344 
345 	sigcount++;
346 }
347 
348 static void
timeout(int s)349 timeout(int s)
350 {
351 
352 }
353 
354 static void
hexdump(uint8_t * ptr,size_t len)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
uart_send_cmd(int fd,uint16_t opcode,void * buf,size_t len)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
uart_getc(int fd,struct iovec * iov,int ioc,size_t * count)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
uart_recv_pkt(int fd,struct iovec * iov,int ioc)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
uart_recv_ev(int fd,uint8_t event,void * buf,size_t len)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
uart_recv_cc(int fd,uint16_t opcode,void * buf,size_t len)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
test(const char * tty,tcflag_t cflag,tcflag_t Cflag)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