xref: /netbsd-src/usr.sbin/btattach/btattach.c (revision e2144167ec858345f163178582d41240778f7de3)
1*e2144167Smlelstv /*	$NetBSD: btattach.c,v 1.16 2023/02/07 20:45:44 mlelstv Exp $	*/
2486e4624Splunky 
3486e4624Splunky /*-
4486e4624Splunky  * Copyright (c) 2008 Iain Hibbert
5486e4624Splunky  * All rights reserved.
6486e4624Splunky  *
7486e4624Splunky  * Redistribution and use in source and binary forms, with or without
8486e4624Splunky  * modification, are permitted provided that the following conditions
9486e4624Splunky  * are met:
10486e4624Splunky  * 1. Redistributions of source code must retain the above copyright
11486e4624Splunky  *    notice, this list of conditions and the following disclaimer.
12486e4624Splunky  * 2. Redistributions in binary form must reproduce the above copyright
13486e4624Splunky  *    notice, this list of conditions and the following disclaimer in the
14486e4624Splunky  *    documentation and/or other materials provided with the distribution.
15486e4624Splunky  *
16486e4624Splunky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17486e4624Splunky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18486e4624Splunky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19486e4624Splunky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20486e4624Splunky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21486e4624Splunky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22486e4624Splunky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23486e4624Splunky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24486e4624Splunky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25486e4624Splunky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26486e4624Splunky  */
27486e4624Splunky 
28486e4624Splunky #include <sys/cdefs.h>
299c194566Slukem __COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert.  All rights reserved.");
30*e2144167Smlelstv __RCSID("$NetBSD: btattach.c,v 1.16 2023/02/07 20:45:44 mlelstv Exp $");
31486e4624Splunky 
32486e4624Splunky #include <sys/ioctl.h>
33486e4624Splunky #include <sys/param.h>
34486e4624Splunky #include <sys/uio.h>
35486e4624Splunky 
36486e4624Splunky #include <bluetooth.h>
37486e4624Splunky #include <err.h>
38486e4624Splunky #include <errno.h>
39486e4624Splunky #include <fcntl.h>
40486e4624Splunky #include <stdio.h>
41486e4624Splunky #include <stdlib.h>
42486e4624Splunky #include <string.h>
43*e2144167Smlelstv #include <signal.h>
44486e4624Splunky #include <termios.h>
45486e4624Splunky #include <unistd.h>
46486e4624Splunky #include <util.h>
47486e4624Splunky 
48486e4624Splunky #include "btattach.h"
49486e4624Splunky 
50486e4624Splunky static void sighandler(int);
5194b1bee2Sjoerg __dead static void usage(void);
5246700600Skiyohara static void test(const char *, tcflag_t, tcflag_t);
53486e4624Splunky 
54486e4624Splunky static int sigcount = 0;	/* signals received */
55486e4624Splunky static int opt_debug = 0;	/* global? */
56486e4624Splunky 
5794b1bee2Sjoerg static const struct devtype types[] = {
58486e4624Splunky     {
59486e4624Splunky 	.name = "bcm2035",
60486e4624Splunky 	.line = "btuart",
61486e4624Splunky 	.descr = "Broadcom BCM2035",
62486e4624Splunky 	.init = &init_bcm2035,
63486e4624Splunky 	.speed = B115200,
64486e4624Splunky     },
65486e4624Splunky     {
66e5d9803bSnat 	.name = "bcm43xx",
676ad4d0f8Sjmcneill 	.line = "btuart",
68e5d9803bSnat 	.descr = "Broadcom BCM43xx",
69e5d9803bSnat 	.init = &init_bcm43xx,
70e5d9803bSnat 	.speed = B115200,
71e5d9803bSnat     },
72e5d9803bSnat     {
736ad4d0f8Sjmcneill 	.name = "bcm43xx-3wire",
746ad4d0f8Sjmcneill 	.line = "bth5",
756ad4d0f8Sjmcneill 	.descr = "Broadcom BCM43xx (3-wire)",
766ad4d0f8Sjmcneill 	.init = &init_bcm43xx,
776ad4d0f8Sjmcneill 	.speed = B115200,
786ad4d0f8Sjmcneill     },
796ad4d0f8Sjmcneill     {
80de7a8fb3Splunky 	.name = "bcsp",
81de7a8fb3Splunky 	.line = "bcsp",
82de7a8fb3Splunky 	.descr = "Generic BlueCore Serial Protocol",
8345be6911Skiyohara 	.cflag = CRTSCTS | PARENB,
84de7a8fb3Splunky 	.speed = B57600,
85de7a8fb3Splunky     },
86de7a8fb3Splunky     {
87486e4624Splunky 	.name = "bgb2xx",
88486e4624Splunky 	.line = "btuart",
89486e4624Splunky 	.descr = "Philips BGB2xx module",
90486e4624Splunky 	.init = &init_bgb2xx,
91486e4624Splunky 	.cflag = CRTSCTS,
92486e4624Splunky 	.speed = B115200,
93486e4624Splunky     },
94486e4624Splunky     {
95486e4624Splunky 	.name = "btuart",
96486e4624Splunky 	.line = "btuart",
97486e4624Splunky 	.descr = "Generic UART (the default)",
98486e4624Splunky     },
99486e4624Splunky     {
100486e4624Splunky 	.name = "csr",
101486e4624Splunky 	.line = "btuart",
102de7a8fb3Splunky 	.descr = "Cambridge Silicon Radio based modules (not BCSP)",
103486e4624Splunky 	.init = &init_csr,
104486e4624Splunky 	.cflag = CRTSCTS,
105486e4624Splunky 	.speed = B57600,
106486e4624Splunky     },
107486e4624Splunky     {
108486e4624Splunky 	.name = "digi",
109486e4624Splunky 	.line = "btuart",
110486e4624Splunky 	.descr = "Digianswer based cards",
111486e4624Splunky 	.init = &init_digi,
112486e4624Splunky 	.cflag = CRTSCTS,
113486e4624Splunky 	.speed = B9600,
114486e4624Splunky     },
115486e4624Splunky     {
116486e4624Splunky 	.name = "ericsson",
117486e4624Splunky 	.line = "btuart",
118486e4624Splunky 	.descr = "Ericsson based modules",
119486e4624Splunky 	.init = &init_ericsson,
120486e4624Splunky 	.cflag = CRTSCTS,
121486e4624Splunky 	.speed = B57600,
122486e4624Splunky     },
123486e4624Splunky     {
124486e4624Splunky 	.name = "st",
125486e4624Splunky 	.line = "btuart",
126486e4624Splunky 	.descr = "ST Microelectronics minikits based on STLC2410/STLC2415",
127486e4624Splunky 	.init = &init_st,
128486e4624Splunky 	.cflag = CRTSCTS,
129486e4624Splunky 	.speed = B57600,
130486e4624Splunky     },
131486e4624Splunky     {
132486e4624Splunky 	.name = "stlc2500",
133486e4624Splunky 	.descr = "ST Microelectronics minikits based on STLC2500",
134486e4624Splunky 	.init = &init_stlc2500,
135486e4624Splunky 	.cflag = CRTSCTS,
136486e4624Splunky 	.speed = B115200,
137486e4624Splunky     },
138486e4624Splunky     {
139486e4624Splunky 	.name = "swave",
140486e4624Splunky 	.line = "btuart",
141486e4624Splunky 	.descr = "Silicon Wave kits",
142486e4624Splunky 	.init = &init_swave,
143486e4624Splunky 	.cflag = CRTSCTS,
144486e4624Splunky 	.speed = B57600,
145486e4624Splunky     },
146486e4624Splunky     {
147486e4624Splunky 	.name = "texas",
148486e4624Splunky 	.line = "btuart",
149486e4624Splunky 	.descr = "Texas Instruments",
150486e4624Splunky 	.cflag = CRTSCTS,
151486e4624Splunky 	.speed = B115200,
152486e4624Splunky     },
1534e74fe32Skiyohara     {
1544e74fe32Skiyohara 	.name = "unistone",
1554e74fe32Skiyohara 	.line = "btuart",
1564e74fe32Skiyohara 	.descr = "Infineon UniStone",
1574e74fe32Skiyohara 	.init = &init_unistone,
1584e74fe32Skiyohara 	.cflag = CRTSCTS,
1594e74fe32Skiyohara 	.speed = B115200,
1604e74fe32Skiyohara     },
161486e4624Splunky };
162486e4624Splunky 
163486e4624Splunky int
main(int argc,char * argv[])164486e4624Splunky main(int argc, char *argv[])
165486e4624Splunky {
166486e4624Splunky 	const struct devtype *type;
167486e4624Splunky 	struct termios tio;
168486e4624Splunky 	unsigned int init_speed, speed;
16945be6911Skiyohara 	tcflag_t cflag, Cflag;
17046700600Skiyohara 	int fd, ch, tflag, i;
171486e4624Splunky 	const char *name;
172486e4624Splunky 	char *ptr;
173486e4624Splunky 
174486e4624Splunky 	init_speed = 0;
175486e4624Splunky 	cflag = CLOCAL;
17645be6911Skiyohara 	Cflag = 0;
17746700600Skiyohara 	tflag = 0;
178486e4624Splunky 	name = "btuart";
179486e4624Splunky 
18046700600Skiyohara 	while ((ch = getopt(argc, argv, "dFfi:oPpt")) != -1) {
181486e4624Splunky 		switch (ch) {
182486e4624Splunky 		case 'd':
183486e4624Splunky 			opt_debug++;
184486e4624Splunky 			break;
185486e4624Splunky 
18645be6911Skiyohara 		case 'F':
18745be6911Skiyohara 			Cflag |= CRTSCTS;
18845be6911Skiyohara 			break;
18945be6911Skiyohara 
190486e4624Splunky 		case 'f':
191486e4624Splunky 			cflag |= CRTSCTS;
192486e4624Splunky 			break;
193486e4624Splunky 
194486e4624Splunky 		case 'i':
195486e4624Splunky 			init_speed = strtoul(optarg, &ptr, 10);
196486e4624Splunky 			if (ptr[0] != '\0')
197486e4624Splunky 				errx(EXIT_FAILURE, "invalid speed: %s", optarg);
198486e4624Splunky 
199486e4624Splunky 			break;
200486e4624Splunky 
201486e4624Splunky 		case 'o':
202486e4624Splunky 			cflag |= (PARENB | PARODD);
203486e4624Splunky 			break;
204486e4624Splunky 
20545be6911Skiyohara 		case 'P':
20645be6911Skiyohara 			Cflag |= PARENB;
20745be6911Skiyohara 			break;
20845be6911Skiyohara 
209486e4624Splunky 		case 'p':
210486e4624Splunky 			cflag |= PARENB;
211486e4624Splunky 			break;
212486e4624Splunky 
21346700600Skiyohara 		case 't':
21446700600Skiyohara 			tflag = 1;
21546700600Skiyohara 			break;
21646700600Skiyohara 
217486e4624Splunky 		case '?':
218486e4624Splunky 		default:
219486e4624Splunky 			usage();
220486e4624Splunky 		}
221486e4624Splunky 	}
222486e4624Splunky 	argc -= optind;
223486e4624Splunky 	argv += optind;
224486e4624Splunky 
22546700600Skiyohara 	if (tflag) {
22646700600Skiyohara 		if (argc != 1)
22746700600Skiyohara 			usage();
22846700600Skiyohara 		test(argv[0], cflag, Cflag);
22946700600Skiyohara 		exit(EXIT_SUCCESS);
23046700600Skiyohara 	}
23146700600Skiyohara 
232486e4624Splunky 	if (argc == 3) {
233486e4624Splunky 		name = argv[0];
234486e4624Splunky 		argv++;
235486e4624Splunky 		argc--;
236486e4624Splunky 	}
237486e4624Splunky 
238486e4624Splunky 	for (i = 0; ; i++) {
239486e4624Splunky 		if (i == __arraycount(types))
240486e4624Splunky 			errx(EXIT_FAILURE, "unknown type: %s", name);
241486e4624Splunky 
242486e4624Splunky 		type = &types[i];
243486e4624Splunky 		if (strcasecmp(type->name, name) == 0)
244486e4624Splunky 			break;
245486e4624Splunky 	}
246486e4624Splunky 
247486e4624Splunky 	if (argc != 2)
248486e4624Splunky 		usage();
249486e4624Splunky 
250486e4624Splunky 	/* parse tty speed */
251486e4624Splunky 	speed = strtoul(argv[1], &ptr, 10);
252486e4624Splunky 	if (ptr[0] != '\0')
253486e4624Splunky 		errx(EXIT_FAILURE, "invalid speed: %s", argv[1]);
254486e4624Splunky 
255486e4624Splunky 	if (init_speed == 0)
256486e4624Splunky 		init_speed = (type->speed ? type->speed : speed);
257486e4624Splunky 
258486e4624Splunky 	/* open tty */
259adcbe4e3Skiyohara 	if ((fd = open(argv[0], O_RDWR | O_EXLOCK, 0)) < 0)
260486e4624Splunky 		err(EXIT_FAILURE, "%s", argv[0]);
261486e4624Splunky 
262486e4624Splunky 	/* setup tty */
263486e4624Splunky 	if (tcgetattr(fd, &tio) < 0)
264486e4624Splunky 		err(EXIT_FAILURE, "tcgetattr");
265486e4624Splunky 
266486e4624Splunky 	cfmakeraw(&tio);
267486e4624Splunky 	tio.c_cflag |= (cflag | type->cflag);
26845be6911Skiyohara 	tio.c_cflag &= ~Cflag;
269486e4624Splunky 
270486e4624Splunky 	if (cfsetspeed(&tio, init_speed) < 0
271486e4624Splunky 	    || tcsetattr(fd, TCSANOW, &tio) < 0
272486e4624Splunky 	    || tcflush(fd, TCIOFLUSH) < 0)
273486e4624Splunky 		err(EXIT_FAILURE, "tty setup failed");
274486e4624Splunky 
275486e4624Splunky 	/* initialize device */
276486e4624Splunky 	if (type->init != NULL)
277486e4624Splunky 		(*type->init)(fd, speed);
278486e4624Splunky 
279*e2144167Smlelstv 	if (speed != init_speed) {
280486e4624Splunky 		if (cfsetspeed(&tio, speed) < 0
281*e2144167Smlelstv 		    || tcsetattr(fd, TCSANOW, &tio) < 0)
282486e4624Splunky 			err(EXIT_FAILURE, "tty setup failed");
283*e2144167Smlelstv 	}
284486e4624Splunky 
285486e4624Splunky 	/* start line discipline */
286486e4624Splunky 	if (ioctl(fd, TIOCSLINED, type->line) < 0)
287486e4624Splunky 		err(EXIT_FAILURE, "%s", type->line);
288486e4624Splunky 
289486e4624Splunky 	if (opt_debug == 0 && daemon(0, 0) < 0)
290486e4624Splunky 		warn("detach failed!");
291486e4624Splunky 
292486e4624Splunky 	/* store PID in "/var/run/btattach-{tty}.pid" */
293486e4624Splunky 	ptr = strrchr(argv[0], '/');
294486e4624Splunky 	asprintf(&ptr, "%s-%s", getprogname(), (ptr ? ptr + 1 : argv[0]));
295486e4624Splunky 	if (ptr == NULL || pidfile(ptr) < 0)
296486e4624Splunky 		warn("no pidfile");
297486e4624Splunky 
298486e4624Splunky 	free(ptr);
299486e4624Splunky 
300486e4624Splunky 	(void)signal(SIGHUP, sighandler);
301486e4624Splunky 	(void)signal(SIGINT, sighandler);
302486e4624Splunky 	(void)signal(SIGTERM, sighandler);
303486e4624Splunky 	(void)signal(SIGTSTP, sighandler);
304486e4624Splunky 	(void)signal(SIGUSR1, sighandler);
305486e4624Splunky 	(void)signal(SIGUSR2, sighandler);
306486e4624Splunky 
307486e4624Splunky 	while (sigcount == 0)
308486e4624Splunky 		select(0, NULL, NULL, NULL, NULL);
309486e4624Splunky 
310486e4624Splunky 	return EXIT_SUCCESS;
311486e4624Splunky }
312486e4624Splunky 
313486e4624Splunky static void
usage(void)314486e4624Splunky usage(void)
315486e4624Splunky {
3164d8dfd66Slukem 	size_t i;
317486e4624Splunky 
318486e4624Splunky 	fprintf(stderr,
31945be6911Skiyohara 		"Usage: %s [-dFfoPp] [-i speed] [type] tty speed\n"
32046700600Skiyohara 		"       %s -t [-dFfoPp] tty\n"
321486e4624Splunky 		"\n"
322486e4624Splunky 		"Where:\n"
323486e4624Splunky 		"\t-d          debug mode (no detach, dump io)\n"
32445be6911Skiyohara 		"\t-F          disable flow control\n"
325486e4624Splunky 		"\t-f          enable flow control\n"
326486e4624Splunky 		"\t-i speed    init speed\n"
327486e4624Splunky 		"\t-o          odd parity\n"
32845be6911Skiyohara 		"\t-P          no parity\n"
329486e4624Splunky 		"\t-p          even parity\n"
33046700600Skiyohara 		"\t-t          test mode\n"
331486e4624Splunky 		"\n"
332486e4624Splunky 		"Known types:\n"
33346700600Skiyohara 		"", getprogname(), getprogname());
334486e4624Splunky 
335486e4624Splunky 	for (i = 0; i < __arraycount(types); i++)
336486e4624Splunky 		fprintf(stderr, "\t%-12s%s\n", types[i].name, types[i].descr);
337486e4624Splunky 
338486e4624Splunky 	exit(EXIT_FAILURE);
339486e4624Splunky }
340486e4624Splunky 
341486e4624Splunky static void
sighandler(int s)342486e4624Splunky sighandler(int s)
343486e4624Splunky {
344486e4624Splunky 
345486e4624Splunky 	sigcount++;
346486e4624Splunky }
347486e4624Splunky 
348486e4624Splunky static void
timeout(int s)349*e2144167Smlelstv timeout(int s)
350*e2144167Smlelstv {
351*e2144167Smlelstv 
352*e2144167Smlelstv }
353*e2144167Smlelstv 
354*e2144167Smlelstv static void
hexdump(uint8_t * ptr,size_t len)355486e4624Splunky hexdump(uint8_t *ptr, size_t len)
356486e4624Splunky {
357486e4624Splunky 
358486e4624Splunky 	while (len--)
359486e4624Splunky 		printf(" %2.2x", *ptr++);
360486e4624Splunky }
361486e4624Splunky 
362486e4624Splunky /*
363486e4624Splunky  * send HCI comamnd
364486e4624Splunky  */
365*e2144167Smlelstv int
uart_send_cmd(int fd,uint16_t opcode,void * buf,size_t len)366486e4624Splunky uart_send_cmd(int fd, uint16_t opcode, void *buf, size_t len)
367486e4624Splunky {
368486e4624Splunky 	struct iovec iov[2];
369486e4624Splunky 	hci_cmd_hdr_t hdr;
370*e2144167Smlelstv 	int r;
371*e2144167Smlelstv 	struct sigaction oaction, taction;
372486e4624Splunky 
373486e4624Splunky 	hdr.type = HCI_CMD_PKT;
374486e4624Splunky 	hdr.opcode = htole16(opcode);
375486e4624Splunky 	hdr.length = len;
376486e4624Splunky 
377486e4624Splunky 	iov[0].iov_base = &hdr;
378486e4624Splunky 	iov[0].iov_len = sizeof(hdr);
379486e4624Splunky 	iov[1].iov_base = buf;
380486e4624Splunky 	iov[1].iov_len = len;
381486e4624Splunky 
382486e4624Splunky 	if (opt_debug) {
383486e4624Splunky 		printf("<<");
384486e4624Splunky 		hexdump(iov[0].iov_base, iov[0].iov_len);
385486e4624Splunky 		hexdump(iov[1].iov_base, iov[1].iov_len);
386486e4624Splunky 		printf("\n");
387486e4624Splunky 		fflush(stdout);
388486e4624Splunky 	}
389486e4624Splunky 
390486e4624Splunky 	if (writev(fd, iov, __arraycount(iov)) < 0)
391486e4624Splunky 		err(EXIT_FAILURE, "writev");
392486e4624Splunky 
393*e2144167Smlelstv 	taction.sa_handler = timeout,
394*e2144167Smlelstv 	sigemptyset(&taction.sa_mask);
395*e2144167Smlelstv 	taction.sa_flags = 0,
396*e2144167Smlelstv 
397*e2144167Smlelstv 	sigaction(SIGALRM, &taction, &oaction);
398*e2144167Smlelstv 	alarm(1);
399*e2144167Smlelstv 	r = tcdrain(fd);
400*e2144167Smlelstv 	alarm(0);
401*e2144167Smlelstv 	sigaction(SIGALRM, &oaction, NULL);
402*e2144167Smlelstv 
403*e2144167Smlelstv 	return r;
404486e4624Splunky }
405486e4624Splunky 
406486e4624Splunky /*
407486e4624Splunky  * get next character
408486e4624Splunky  * store in iovec and inc counter if it fits
409486e4624Splunky  */
410486e4624Splunky static uint8_t
uart_getc(int fd,struct iovec * iov,int ioc,size_t * count)411486e4624Splunky uart_getc(int fd, struct iovec *iov, int ioc, size_t *count)
412486e4624Splunky {
413486e4624Splunky 	uint8_t ch, *b;
414486e4624Splunky 	ssize_t n;
415486e4624Splunky 	size_t off;
416486e4624Splunky 
417486e4624Splunky 	n = read(fd, &ch, sizeof(ch));
418486e4624Splunky 	if (n < 0)
419486e4624Splunky 		err(EXIT_FAILURE, "read");
420486e4624Splunky 
421486e4624Splunky 	if (n == 0)
422486e4624Splunky 		errx(EXIT_FAILURE, "eof");
423486e4624Splunky 
424486e4624Splunky 	if (opt_debug)
425486e4624Splunky 		printf(" %2.2x", ch);
426486e4624Splunky 
427486e4624Splunky 	off = *count;
428486e4624Splunky 	while (ioc > 0) {
429486e4624Splunky 		if (iov->iov_len > off) {
430486e4624Splunky 			b = iov->iov_base;
431486e4624Splunky 			b[off] = ch;
432486e4624Splunky 			*count += 1;
433486e4624Splunky 			break;
434486e4624Splunky 		}
435486e4624Splunky 
436486e4624Splunky 		off -= iov->iov_len;
437486e4624Splunky 		iov++;
438486e4624Splunky 		ioc--;
439486e4624Splunky 	}
440486e4624Splunky 
441486e4624Splunky 	return ch;
442486e4624Splunky }
443486e4624Splunky 
444486e4624Splunky /*
445486e4624Splunky  * read next packet, storing into iovec
446486e4624Splunky  */
447486e4624Splunky static size_t
uart_recv_pkt(int fd,struct iovec * iov,int ioc)448486e4624Splunky uart_recv_pkt(int fd, struct iovec *iov, int ioc)
449486e4624Splunky {
450486e4624Splunky 	size_t count, want;
451486e4624Splunky 	uint8_t type;
452486e4624Splunky 
453486e4624Splunky 	if (opt_debug)
454486e4624Splunky 		printf(">>");
455486e4624Splunky 
456486e4624Splunky 	count = 0;
457486e4624Splunky 	type = uart_getc(fd, iov, ioc, &count);
458486e4624Splunky 	switch(type) {
459486e4624Splunky 	case HCI_EVENT_PKT:
460486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);	/* event */
461adcbe4e3Skiyohara 		want = uart_getc(fd, iov, ioc, &count);
462486e4624Splunky 		break;
463486e4624Splunky 
464486e4624Splunky 	case HCI_ACL_DATA_PKT:
465486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);	/* handle LSB */
466486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);	/* handle MSB */
467adcbe4e3Skiyohara 		want = uart_getc(fd, iov, ioc, &count) |	/* LSB */
468adcbe4e3Skiyohara 		  uart_getc(fd, iov, ioc, &count) << 8;		/* MSB */
469486e4624Splunky 		break;
470486e4624Splunky 
471486e4624Splunky 	case HCI_SCO_DATA_PKT:
472486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);	/* handle LSB */
473486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);	/* handle MSB */
474adcbe4e3Skiyohara 		want = uart_getc(fd, iov, ioc, &count);
475486e4624Splunky 		break;
476486e4624Splunky 
477486e4624Splunky 	default: /* out of sync? */
4788ee626c9Schristos 		errx(EXIT_FAILURE, "unknown packet type 0x%2.2x", type);
479486e4624Splunky 	}
480486e4624Splunky 
481486e4624Splunky 	while (want-- > 0)
482486e4624Splunky 		(void)uart_getc(fd, iov, ioc, &count);
483486e4624Splunky 
484486e4624Splunky 	if (opt_debug)
485486e4624Splunky 		printf("\n");
486486e4624Splunky 
487486e4624Splunky 	return count;
488486e4624Splunky }
489486e4624Splunky 
490486e4624Splunky /*
491486e4624Splunky  * read next matching event packet to buffer
492486e4624Splunky  */
493486e4624Splunky size_t
uart_recv_ev(int fd,uint8_t event,void * buf,size_t len)494486e4624Splunky uart_recv_ev(int fd, uint8_t event, void *buf, size_t len)
495486e4624Splunky {
496486e4624Splunky 	struct iovec iov[2];
497486e4624Splunky 	hci_event_hdr_t hdr;
498486e4624Splunky 	size_t n;
499486e4624Splunky 
500486e4624Splunky 	iov[0].iov_base = &hdr;
501486e4624Splunky 	iov[0].iov_len = sizeof(hdr);
502486e4624Splunky 	iov[1].iov_base = buf;
503486e4624Splunky 	iov[1].iov_len = len;
504486e4624Splunky 
505486e4624Splunky 	for (;;) {
506486e4624Splunky 		n = uart_recv_pkt(fd, iov, __arraycount(iov));
507486e4624Splunky 		if (n < sizeof(hdr)
508486e4624Splunky 		    || hdr.type != HCI_EVENT_PKT
509486e4624Splunky 		    || hdr.event != event)
510486e4624Splunky 			continue;
511486e4624Splunky 
512486e4624Splunky 		n -= sizeof(hdr);
513486e4624Splunky 		break;
514486e4624Splunky 	}
515486e4624Splunky 
516486e4624Splunky 	return n;
517486e4624Splunky }
518486e4624Splunky 
519486e4624Splunky /*
520486e4624Splunky  * read next matching command_complete event to buffer
521486e4624Splunky  */
522486e4624Splunky size_t
uart_recv_cc(int fd,uint16_t opcode,void * buf,size_t len)523486e4624Splunky uart_recv_cc(int fd, uint16_t opcode, void *buf, size_t len)
524486e4624Splunky {
525486e4624Splunky 	struct iovec iov[3];
526486e4624Splunky 	hci_event_hdr_t hdr;
527486e4624Splunky 	hci_command_compl_ep cc;
528486e4624Splunky 	size_t n;
529486e4624Splunky 
530486e4624Splunky 	iov[0].iov_base = &hdr;
531486e4624Splunky 	iov[0].iov_len = sizeof(hdr);
532486e4624Splunky 	iov[1].iov_base = &cc;
533486e4624Splunky 	iov[1].iov_len = sizeof(cc);
534486e4624Splunky 	iov[2].iov_base = buf;
535486e4624Splunky 	iov[2].iov_len = len;
536486e4624Splunky 
537486e4624Splunky 	for (;;) {
538486e4624Splunky 		n = uart_recv_pkt(fd, iov, __arraycount(iov));
539486e4624Splunky 		if (n < sizeof(hdr)
540486e4624Splunky 		    || hdr.type != HCI_EVENT_PKT
541486e4624Splunky 		    || hdr.event != HCI_EVENT_COMMAND_COMPL)
542486e4624Splunky 			continue;
543486e4624Splunky 
544486e4624Splunky 		n -= sizeof(hdr);
545486e4624Splunky 		if (n < sizeof(cc)
546486e4624Splunky 		    || cc.opcode != htole16(opcode))
547486e4624Splunky 			continue;
548486e4624Splunky 
549486e4624Splunky 		n -= sizeof(cc);
550486e4624Splunky 		break;
551486e4624Splunky 	}
552486e4624Splunky 
553486e4624Splunky 	return n;
554486e4624Splunky }
55546700600Skiyohara 
55646700600Skiyohara static void
test(const char * tty,tcflag_t cflag,tcflag_t Cflag)55746700600Skiyohara test(const char *tty, tcflag_t cflag, tcflag_t Cflag)
55846700600Skiyohara {
55946700600Skiyohara 	struct termios tio;
5607937d424Splunky 	int fd, guessed;
5617937d424Splunky 	size_t i, j, k;
5627937d424Splunky 	ssize_t n;
56346700600Skiyohara 	unsigned char buf[32];
56446700600Skiyohara 	const int bauds[] = {
56546700600Skiyohara 		 57600,		/* BCSP specific default */
56646700600Skiyohara 		921600,		/* latest major baud rate */
56746700600Skiyohara 		115200,		/* old major baud rate */
56846700600Skiyohara 
56946700600Skiyohara 		460800,
57046700600Skiyohara 		230400,
57146700600Skiyohara //		 76800,
57246700600Skiyohara 		 28800,
57346700600Skiyohara 		 38400,
57446700600Skiyohara 		 19200,
57546700600Skiyohara 		 14400,
57646700600Skiyohara 		  9600,
57746700600Skiyohara 		  7200,
57846700600Skiyohara 		  4800,
57946700600Skiyohara 		  2400,
58046700600Skiyohara 		  1800,
58146700600Skiyohara 		  1200,
58246700600Skiyohara 		   600,
58346700600Skiyohara 		   300,
58446700600Skiyohara 		   200,
58546700600Skiyohara 		   150,
58646700600Skiyohara 		   134,
58746700600Skiyohara 		   110,
58846700600Skiyohara 		    75,
58946700600Skiyohara 		    50,
59046700600Skiyohara 	};
59146700600Skiyohara 	const unsigned char bcsp_lepkt[] =
59246700600Skiyohara 	    /* ESC  ------- header -------  --- link establish ---   ESC */
59346700600Skiyohara 	    { 0xc0, 0x00, 0x41, 0x00, 0xbe, 0xda, 0xdc, 0xed, 0xed, 0xc0 };
59446700600Skiyohara 
59546700600Skiyohara 	printf("test mode\n");
59646700600Skiyohara 
59746700600Skiyohara 	/* open tty */
59846700600Skiyohara 	if ((fd = open(tty, O_RDWR | O_NONBLOCK | O_EXLOCK, 0)) < 0)
59946700600Skiyohara 		err(EXIT_FAILURE, "%s", tty);
60046700600Skiyohara 
60146700600Skiyohara 	/* setup tty */
60246700600Skiyohara 	if (tcgetattr(fd, &tio) < 0)
60346700600Skiyohara 		err(EXIT_FAILURE, "tcgetattr");
60446700600Skiyohara 	cfmakeraw(&tio);
60546700600Skiyohara 	tio.c_cflag |= (CLOCAL | CRTSCTS | PARENB);
60646700600Skiyohara 	tio.c_cflag |= cflag;
60746700600Skiyohara 	tio.c_cflag &= ~Cflag;
60846700600Skiyohara 
60946700600Skiyohara 	guessed = 0;
61046700600Skiyohara 	for (i = 0; i < __arraycount(bauds); i++) {
61146700600Skiyohara 		if (cfsetspeed(&tio, bauds[i]) < 0
61246700600Skiyohara 		    || tcsetattr(fd, TCSANOW, &tio) < 0
6137937d424Splunky 		    || tcflush(fd, TCIOFLUSH) < 0) {
61446700600Skiyohara 			if (bauds[i] > 115200)
61546700600Skiyohara 				continue;
61646700600Skiyohara 			else
61746700600Skiyohara 				err(EXIT_FAILURE, "tty setup failed");
6187937d424Splunky 		}
61946700600Skiyohara 
62046700600Skiyohara 		if (opt_debug)
62146700600Skiyohara 			printf("  try with B%d\n", bauds[i]);
62246700600Skiyohara 
62346700600Skiyohara 		sleep(bauds[i] < 9600 ? 3 : 1);
62446700600Skiyohara 
62546700600Skiyohara 		n = read(fd, buf, sizeof(buf));
62646700600Skiyohara 		if (opt_debug > 1)
627c683e41bSplunky 			printf("  %zd bytes read\n", n);
62846700600Skiyohara 		if (n < 0) {
62946700600Skiyohara 			if (i == 0 && errno == EAGAIN) {
63046700600Skiyohara 				printf("This module is *maybe* supported by btuart(4).\n"
63146700600Skiyohara 				    "you specify aproporiate <speed>.\n"
63246700600Skiyohara 				    "  Also can specify <type> for initialize.\n");
63346700600Skiyohara 				guessed = 1;
63446700600Skiyohara 				break;
63546700600Skiyohara 			}
63646700600Skiyohara 			if (errno == EAGAIN)
63746700600Skiyohara 				continue;
63846700600Skiyohara 
63946700600Skiyohara 			err(EXIT_FAILURE, "read");
64046700600Skiyohara 		} else {
6417937d424Splunky 			if ((size_t)n < sizeof(bcsp_lepkt))
64246700600Skiyohara 				continue;
64346700600Skiyohara 			for (j = 0; j < n - sizeof(bcsp_lepkt); j++) {
64446700600Skiyohara 				for (k = 0; k < sizeof(bcsp_lepkt); k++)
64546700600Skiyohara 					if (buf[j + k] != bcsp_lepkt[k]) {
64646700600Skiyohara 						j += k;
64746700600Skiyohara 						break;
64846700600Skiyohara 					}
64946700600Skiyohara 				if (k < sizeof(bcsp_lepkt))
65046700600Skiyohara 					continue;
65146700600Skiyohara 
65246700600Skiyohara 				printf(
65346700600Skiyohara 				    "This module is supported by bcsp(4).\n"
65446700600Skiyohara 				    "  baud rate %d\n",
65546700600Skiyohara 				    bauds[i]);
65646700600Skiyohara 				if (tio.c_cflag & PARENB)
65746700600Skiyohara 					printf("  with %sparity\n",
65846700600Skiyohara 					    tio.c_cflag & PARODD ? "odd " : "");
65946700600Skiyohara 				guessed = 1;
66046700600Skiyohara 				break;
66146700600Skiyohara 			}
66246700600Skiyohara 			if (guessed)
66346700600Skiyohara 				break;
66446700600Skiyohara 		}
66546700600Skiyohara 
66646700600Skiyohara 	}
66746700600Skiyohara 
66846700600Skiyohara 	close(fd);
66946700600Skiyohara 
67046700600Skiyohara 	if (!guessed)
67146700600Skiyohara 		printf("don't understand...\n");
67246700600Skiyohara }
673