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
50static void sighandler(int);
51__dead static void usage(void);
52static void test(const char *, tcflag_t, tcflag_t);
53
54static int sigcount = 0;	/* signals received */
55static int opt_debug = 0;	/* global? */
56
57static 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
163int
164main(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
313static void
314usage(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
341static void
342sighandler(int s)
343{
344
345	sigcount++;
346}
347
348static void
349timeout(int s)
350{
351
352}
353
354static void
355hexdump(uint8_t *ptr, size_t len)
356{
357
358	while (len--)
359		printf(" %2.2x", *ptr++);
360}
361
362/*
363 * send HCI comamnd
364 */
365int
366uart_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 */
410static uint8_t
411uart_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 */
447static size_t
448uart_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 */
493size_t
494uart_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 */
522size_t
523uart_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
556static void
557test(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