btattach.c revision 1.15 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