btattach.c revision 1.10 1 /* $NetBSD: btattach.c,v 1.10 2010/03/08 21:48:42 plunky 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.10 2010/03/08 21:48:42 plunky 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 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 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 = "bcsp",
66 .line = "bcsp",
67 .descr = "Generic BlueCore Serial Protocol",
68 .cflag = CRTSCTS | PARENB,
69 .speed = B57600,
70 },
71 {
72 .name = "bgb2xx",
73 .line = "btuart",
74 .descr = "Philips BGB2xx module",
75 .init = &init_bgb2xx,
76 .cflag = CRTSCTS,
77 .speed = B115200,
78 },
79 {
80 .name = "btuart",
81 .line = "btuart",
82 .descr = "Generic UART (the default)",
83 },
84 {
85 .name = "csr",
86 .line = "btuart",
87 .descr = "Cambridge Silicon Radio based modules (not BCSP)",
88 .init = &init_csr,
89 .cflag = CRTSCTS,
90 .speed = B57600,
91 },
92 {
93 .name = "digi",
94 .line = "btuart",
95 .descr = "Digianswer based cards",
96 .init = &init_digi,
97 .cflag = CRTSCTS,
98 .speed = B9600,
99 },
100 {
101 .name = "ericsson",
102 .line = "btuart",
103 .descr = "Ericsson based modules",
104 .init = &init_ericsson,
105 .cflag = CRTSCTS,
106 .speed = B57600,
107 },
108 {
109 .name = "st",
110 .line = "btuart",
111 .descr = "ST Microelectronics minikits based on STLC2410/STLC2415",
112 .init = &init_st,
113 .cflag = CRTSCTS,
114 .speed = B57600,
115 },
116 {
117 .name = "stlc2500",
118 .descr = "ST Microelectronics minikits based on STLC2500",
119 .init = &init_stlc2500,
120 .cflag = CRTSCTS,
121 .speed = B115200,
122 },
123 {
124 .name = "swave",
125 .line = "btuart",
126 .descr = "Silicon Wave kits",
127 .init = &init_swave,
128 .cflag = CRTSCTS,
129 .speed = B57600,
130 },
131 {
132 .name = "texas",
133 .line = "btuart",
134 .descr = "Texas Instruments",
135 .cflag = CRTSCTS,
136 .speed = B115200,
137 },
138 {
139 .name = "unistone",
140 .line = "btuart",
141 .descr = "Infineon UniStone",
142 .init = &init_unistone,
143 .cflag = CRTSCTS,
144 .speed = B115200,
145 },
146 };
147
148 int
149 main(int argc, char *argv[])
150 {
151 const struct devtype *type;
152 struct termios tio;
153 unsigned int init_speed, speed;
154 tcflag_t cflag, Cflag;
155 int fd, ch, tflag, i;
156 const char *name;
157 char *ptr;
158
159 init_speed = 0;
160 cflag = CLOCAL;
161 Cflag = 0;
162 tflag = 0;
163 name = "btuart";
164
165 while ((ch = getopt(argc, argv, "dFfi:oPpt")) != -1) {
166 switch (ch) {
167 case 'd':
168 opt_debug++;
169 break;
170
171 case 'F':
172 Cflag |= CRTSCTS;
173 break;
174
175 case 'f':
176 cflag |= CRTSCTS;
177 break;
178
179 case 'i':
180 init_speed = strtoul(optarg, &ptr, 10);
181 if (ptr[0] != '\0')
182 errx(EXIT_FAILURE, "invalid speed: %s", optarg);
183
184 break;
185
186 case 'o':
187 cflag |= (PARENB | PARODD);
188 break;
189
190 case 'P':
191 Cflag |= PARENB;
192 break;
193
194 case 'p':
195 cflag |= PARENB;
196 break;
197
198 case 't':
199 tflag = 1;
200 break;
201
202 case '?':
203 default:
204 usage();
205 }
206 }
207 argc -= optind;
208 argv += optind;
209
210 if (tflag) {
211 if (argc != 1)
212 usage();
213 test(argv[0], cflag, Cflag);
214 exit(EXIT_SUCCESS);
215 }
216
217 if (argc == 3) {
218 name = argv[0];
219 argv++;
220 argc--;
221 }
222
223 for (i = 0; ; i++) {
224 if (i == __arraycount(types))
225 errx(EXIT_FAILURE, "unknown type: %s", name);
226
227 type = &types[i];
228 if (strcasecmp(type->name, name) == 0)
229 break;
230 }
231
232 if (argc != 2)
233 usage();
234
235 /* parse tty speed */
236 speed = strtoul(argv[1], &ptr, 10);
237 if (ptr[0] != '\0')
238 errx(EXIT_FAILURE, "invalid speed: %s", argv[1]);
239
240 if (init_speed == 0)
241 init_speed = (type->speed ? type->speed : speed);
242
243 /* open tty */
244 if ((fd = open(argv[0], O_RDWR | O_EXLOCK, 0)) < 0)
245 err(EXIT_FAILURE, "%s", argv[0]);
246
247 /* setup tty */
248 if (tcgetattr(fd, &tio) < 0)
249 err(EXIT_FAILURE, "tcgetattr");
250
251 cfmakeraw(&tio);
252 tio.c_cflag |= (cflag | type->cflag);
253 tio.c_cflag &= ~Cflag;
254
255 if (cfsetspeed(&tio, init_speed) < 0
256 || tcsetattr(fd, TCSANOW, &tio) < 0
257 || tcflush(fd, TCIOFLUSH) < 0)
258 err(EXIT_FAILURE, "tty setup failed");
259
260 /* initialize device */
261 if (type->init != NULL)
262 (*type->init)(fd, speed);
263
264 if (cfsetspeed(&tio, speed) < 0
265 || tcsetattr(fd, TCSADRAIN, &tio) < 0)
266 err(EXIT_FAILURE, "tty setup failed");
267
268 /* start line discipline */
269 if (ioctl(fd, TIOCSLINED, type->line) < 0)
270 err(EXIT_FAILURE, "%s", type->line);
271
272 if (opt_debug == 0 && daemon(0, 0) < 0)
273 warn("detach failed!");
274
275 /* store PID in "/var/run/btattach-{tty}.pid" */
276 ptr = strrchr(argv[0], '/');
277 asprintf(&ptr, "%s-%s", getprogname(), (ptr ? ptr + 1 : argv[0]));
278 if (ptr == NULL || pidfile(ptr) < 0)
279 warn("no pidfile");
280
281 free(ptr);
282
283 (void)signal(SIGHUP, sighandler);
284 (void)signal(SIGINT, sighandler);
285 (void)signal(SIGTERM, sighandler);
286 (void)signal(SIGTSTP, sighandler);
287 (void)signal(SIGUSR1, sighandler);
288 (void)signal(SIGUSR2, sighandler);
289
290 while (sigcount == 0)
291 select(0, NULL, NULL, NULL, NULL);
292
293 return EXIT_SUCCESS;
294 }
295
296 static void
297 usage(void)
298 {
299 size_t i;
300
301 fprintf(stderr,
302 "Usage: %s [-dFfoPp] [-i speed] [type] tty speed\n"
303 " %s -t [-dFfoPp] tty\n"
304 "\n"
305 "Where:\n"
306 "\t-d debug mode (no detach, dump io)\n"
307 "\t-F disable flow control\n"
308 "\t-f enable flow control\n"
309 "\t-i speed init speed\n"
310 "\t-o odd parity\n"
311 "\t-P no parity\n"
312 "\t-p even parity\n"
313 "\t-t test mode\n"
314 "\n"
315 "Known types:\n"
316 "", getprogname(), getprogname());
317
318 for (i = 0; i < __arraycount(types); i++)
319 fprintf(stderr, "\t%-12s%s\n", types[i].name, types[i].descr);
320
321 exit(EXIT_FAILURE);
322 }
323
324 static void
325 sighandler(int s)
326 {
327
328 sigcount++;
329 }
330
331 static void
332 hexdump(uint8_t *ptr, size_t len)
333 {
334
335 while (len--)
336 printf(" %2.2x", *ptr++);
337 }
338
339 /*
340 * send HCI comamnd
341 */
342 void
343 uart_send_cmd(int fd, uint16_t opcode, void *buf, size_t len)
344 {
345 struct iovec iov[2];
346 hci_cmd_hdr_t hdr;
347
348 hdr.type = HCI_CMD_PKT;
349 hdr.opcode = htole16(opcode);
350 hdr.length = len;
351
352 iov[0].iov_base = &hdr;
353 iov[0].iov_len = sizeof(hdr);
354 iov[1].iov_base = buf;
355 iov[1].iov_len = len;
356
357 if (opt_debug) {
358 printf("<<");
359 hexdump(iov[0].iov_base, iov[0].iov_len);
360 hexdump(iov[1].iov_base, iov[1].iov_len);
361 printf("\n");
362 fflush(stdout);
363 }
364
365 if (writev(fd, iov, __arraycount(iov)) < 0)
366 err(EXIT_FAILURE, "writev");
367
368 tcdrain(fd);
369 }
370
371 /*
372 * get next character
373 * store in iovec and inc counter if it fits
374 */
375 static uint8_t
376 uart_getc(int fd, struct iovec *iov, int ioc, size_t *count)
377 {
378 uint8_t ch, *b;
379 ssize_t n;
380 size_t off;
381
382 n = read(fd, &ch, sizeof(ch));
383 if (n < 0)
384 err(EXIT_FAILURE, "read");
385
386 if (n == 0)
387 errx(EXIT_FAILURE, "eof");
388
389 if (opt_debug)
390 printf(" %2.2x", ch);
391
392 off = *count;
393 while (ioc > 0) {
394 if (iov->iov_len > off) {
395 b = iov->iov_base;
396 b[off] = ch;
397 *count += 1;
398 break;
399 }
400
401 off -= iov->iov_len;
402 iov++;
403 ioc--;
404 }
405
406 return ch;
407 }
408
409 /*
410 * read next packet, storing into iovec
411 */
412 static size_t
413 uart_recv_pkt(int fd, struct iovec *iov, int ioc)
414 {
415 size_t count, want;
416 uint8_t type;
417
418 if (opt_debug)
419 printf(">>");
420
421 count = 0;
422 type = uart_getc(fd, iov, ioc, &count);
423 switch(type) {
424 case HCI_EVENT_PKT:
425 (void)uart_getc(fd, iov, ioc, &count); /* event */
426 want = uart_getc(fd, iov, ioc, &count);
427 break;
428
429 case HCI_ACL_DATA_PKT:
430 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */
431 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */
432 want = uart_getc(fd, iov, ioc, &count) | /* LSB */
433 uart_getc(fd, iov, ioc, &count) << 8; /* MSB */
434 break;
435
436 case HCI_SCO_DATA_PKT:
437 (void)uart_getc(fd, iov, ioc, &count); /* handle LSB */
438 (void)uart_getc(fd, iov, ioc, &count); /* handle MSB */
439 want = uart_getc(fd, iov, ioc, &count);
440 break;
441
442 default: /* out of sync? */
443 errx(EXIT_FAILURE, "unknown packet type 0x%2.2x\n", type);
444 }
445
446 while (want-- > 0)
447 (void)uart_getc(fd, iov, ioc, &count);
448
449 if (opt_debug)
450 printf("\n");
451
452 return count;
453 }
454
455 /*
456 * read next matching event packet to buffer
457 */
458 size_t
459 uart_recv_ev(int fd, uint8_t event, void *buf, size_t len)
460 {
461 struct iovec iov[2];
462 hci_event_hdr_t hdr;
463 size_t n;
464
465 iov[0].iov_base = &hdr;
466 iov[0].iov_len = sizeof(hdr);
467 iov[1].iov_base = buf;
468 iov[1].iov_len = len;
469
470 for (;;) {
471 n = uart_recv_pkt(fd, iov, __arraycount(iov));
472 if (n < sizeof(hdr)
473 || hdr.type != HCI_EVENT_PKT
474 || hdr.event != event)
475 continue;
476
477 n -= sizeof(hdr);
478 break;
479 }
480
481 return n;
482 }
483
484 /*
485 * read next matching command_complete event to buffer
486 */
487 size_t
488 uart_recv_cc(int fd, uint16_t opcode, void *buf, size_t len)
489 {
490 struct iovec iov[3];
491 hci_event_hdr_t hdr;
492 hci_command_compl_ep cc;
493 size_t n;
494
495 iov[0].iov_base = &hdr;
496 iov[0].iov_len = sizeof(hdr);
497 iov[1].iov_base = &cc;
498 iov[1].iov_len = sizeof(cc);
499 iov[2].iov_base = buf;
500 iov[2].iov_len = len;
501
502 for (;;) {
503 n = uart_recv_pkt(fd, iov, __arraycount(iov));
504 if (n < sizeof(hdr)
505 || hdr.type != HCI_EVENT_PKT
506 || hdr.event != HCI_EVENT_COMMAND_COMPL)
507 continue;
508
509 n -= sizeof(hdr);
510 if (n < sizeof(cc)
511 || cc.opcode != htole16(opcode))
512 continue;
513
514 n -= sizeof(cc);
515 break;
516 }
517
518 return n;
519 }
520
521 static void
522 test(const char *tty, tcflag_t cflag, tcflag_t Cflag)
523 {
524 struct termios tio;
525 int fd, guessed;
526 size_t i, j, k;
527 ssize_t n;
528 unsigned char buf[32];
529 const int bauds[] = {
530 57600, /* BCSP specific default */
531 921600, /* latest major baud rate */
532 115200, /* old major baud rate */
533
534 460800,
535 230400,
536 // 76800,
537 28800,
538 38400,
539 19200,
540 14400,
541 9600,
542 7200,
543 4800,
544 2400,
545 1800,
546 1200,
547 600,
548 300,
549 200,
550 150,
551 134,
552 110,
553 75,
554 50,
555 };
556 const unsigned char bcsp_lepkt[] =
557 /* ESC ------- header ------- --- link establish --- ESC */
558 { 0xc0, 0x00, 0x41, 0x00, 0xbe, 0xda, 0xdc, 0xed, 0xed, 0xc0 };
559
560 printf("test mode\n");
561
562 /* open tty */
563 if ((fd = open(tty, O_RDWR | O_NONBLOCK | O_EXLOCK, 0)) < 0)
564 err(EXIT_FAILURE, "%s", tty);
565
566 /* setup tty */
567 if (tcgetattr(fd, &tio) < 0)
568 err(EXIT_FAILURE, "tcgetattr");
569 cfmakeraw(&tio);
570 tio.c_cflag |= (CLOCAL | CRTSCTS | PARENB);
571 tio.c_cflag |= cflag;
572 tio.c_cflag &= ~Cflag;
573
574 guessed = 0;
575 for (i = 0; i < __arraycount(bauds); i++) {
576 if (cfsetspeed(&tio, bauds[i]) < 0
577 || tcsetattr(fd, TCSANOW, &tio) < 0
578 || tcflush(fd, TCIOFLUSH) < 0) {
579 if (bauds[i] > 115200)
580 continue;
581 else
582 err(EXIT_FAILURE, "tty setup failed");
583 }
584
585 if (opt_debug)
586 printf(" try with B%d\n", bauds[i]);
587
588 sleep(bauds[i] < 9600 ? 3 : 1);
589
590 n = read(fd, buf, sizeof(buf));
591 if (opt_debug > 1)
592 printf(" %zd bytes read\n", n);
593 if (n < 0) {
594 if (i == 0 && errno == EAGAIN) {
595 printf("This module is *maybe* supported by btuart(4).\n"
596 "you specify aproporiate <speed>.\n"
597 " Also can specify <type> for initialize.\n");
598 guessed = 1;
599 break;
600 }
601 if (errno == EAGAIN)
602 continue;
603
604 err(EXIT_FAILURE, "read");
605 } else {
606 if ((size_t)n < sizeof(bcsp_lepkt))
607 continue;
608 for (j = 0; j < n - sizeof(bcsp_lepkt); j++) {
609 for (k = 0; k < sizeof(bcsp_lepkt); k++)
610 if (buf[j + k] != bcsp_lepkt[k]) {
611 j += k;
612 break;
613 }
614 if (k < sizeof(bcsp_lepkt))
615 continue;
616
617 printf(
618 "This module is supported by bcsp(4).\n"
619 " baud rate %d\n",
620 bauds[i]);
621 if (tio.c_cflag & PARENB)
622 printf(" with %sparity\n",
623 tio.c_cflag & PARODD ? "odd " : "");
624 guessed = 1;
625 break;
626 }
627 if (guessed)
628 break;
629 }
630
631 }
632
633 close(fd);
634
635 if (!guessed)
636 printf("don't understand...\n");
637 }
638