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