xref: /dragonfly/usr.sbin/btconfig/btconfig.c (revision 118a6251)
1 /* $NetBSD: btconfig.c,v 1.6 2007/09/07 18:40:01 plunky Exp $ */
2 
3 /*-
4  * Copyright (c) 2006 Itronix Inc.
5  * All rights reserved.
6  *
7  * Written by Iain Hibbert for Itronix Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of Itronix Inc. may not be used to endorse
18  *    or promote products derived from this software without specific
19  *    prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 
39 #include <net/if.h>
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <err.h>
49 #include <bluetooth.h>
50 #include <netbt/hci.h>
51 #include <sys/endian.h>
52 
53 /* inquiry results storage */
54 struct result {
55 	bdaddr_t	bdaddr;
56 	uint8_t		page_scan_rep_mode;
57 	uint8_t		uclass[HCI_CLASS_SIZE];
58 	uint16_t	clock_offset;
59 	int8_t		rssi;
60 };
61 
62 static void badarg(const char *) __dead2;
63 static void badparam(const char *) __dead2;
64 static void usage(void) __dead2;
65 static int set_unit(unsigned long);
66 static void config_unit(void);
67 static void print_info(int);
68 static void print_stats(void);
69 static void print_class(const char *);
70 static void print_voice(int);
71 static void tag(const char *);
72 static void print_features(const char *, uint8_t *);
73 static void do_inquiry(void);
74 static void print_result(int, struct result *, int);
75 static void printb(uint16_t v, const char *bits);
76 
77 static void hci_req(uint16_t, uint8_t , void *, size_t, void *, size_t);
78 #define save_value(opcode, cbuf, clen)	hci_req(opcode, 0, cbuf, clen, NULL, 0)
79 #define load_value(opcode, rbuf, rlen)	hci_req(opcode, 0, NULL, 0, rbuf, rlen)
80 #define hci_cmd(opcode, cbuf, clen)	hci_req(opcode, 0, cbuf, clen, NULL, 0)
81 
82 #define MAX_STR_SIZE	0xff
83 
84 /* print width */
85 static int width = 0;
86 #define MAX_WIDTH	70
87 
88 /* global variables */
89 static int hci;
90 static struct btreq btr;
91 
92 /* command line flags */
93 static int verbose = 0;	/* more info */
94 static int lflag = 0;	/* list devices */
95 static int sflag = 0;	/* get/zero stats */
96 
97 /* device up/down (flag) */
98 static int opt_enable = 0;
99 static int opt_reset = 0;
100 #define FLAGBITS	"\001UP"		\
101 			"\002RUNNING"		\
102 			"\003XMIT_CMD"		\
103 			"\004XMIT_ACL"		\
104 			"\005XMIT_SCO"		\
105 			"\006INIT_BDADDR"	\
106 			"\007INIT_BUFFER_SIZE"	\
107 			"\010INIT_FEATURES"
108 
109 /* authorisation (flag) */
110 static int opt_auth = 0;
111 
112 /* encryption (flag) */
113 static int opt_encrypt = 0;
114 
115 /* scan enable options (flags) */
116 static int opt_pscan = 0;
117 static int opt_iscan = 0;
118 
119 /* link policy options (flags) */
120 static int opt_switch = 0;
121 static int opt_hold = 0;
122 static int opt_sniff = 0;
123 static int opt_park = 0;
124 
125 /* class of device (hex value) */
126 static int opt_class = 0;
127 static uint32_t class;
128 
129 /* packet type mask (hex value) */
130 static int opt_ptype = 0;
131 static uint32_t ptype;
132 
133 /* unit name (string) */
134 static int opt_name = 0;
135 static char name[MAX_STR_SIZE];
136 
137 /* pin type */
138 static int opt_pin = 0;
139 
140 /* Inquiry */
141 static int opt_rssi = 0;			/* inquiry_with_rssi (flag) */
142 static int opt_inquiry = 0;
143 #define INQUIRY_LENGTH		10	/* about 12 seconds */
144 #define INQUIRY_MAX_RESPONSES	10
145 
146 /* Voice Settings */
147 static int opt_voice = 0;
148 static uint32_t voice;
149 
150 /* Page Timeout */
151 static int opt_pto = 0;
152 static uint32_t pto;
153 
154 /* set SCO mtu */
155 static int opt_scomtu;
156 static uint32_t scomtu;
157 
158 static struct parameter {
159 	const char	*name;
160 	enum { P_SET, P_CLR, P_STR, P_HEX, P_NUM } type;
161 	int		*opt;
162 	void		*val;
163 } parameters[] = {
164 	{ "up",		P_SET,	&opt_enable,	NULL	},
165 	{ "enable",	P_SET,	&opt_enable,	NULL	},
166 	{ "down",	P_CLR,	&opt_enable,	NULL	},
167 	{ "disable",	P_CLR,	&opt_enable,	NULL	},
168 	{ "name",	P_STR,	&opt_name,	name	},
169 	{ "pscan",	P_SET,	&opt_pscan,	NULL	},
170 	{ "-pscan",	P_CLR,	&opt_pscan,	NULL	},
171 	{ "iscan",	P_SET,	&opt_iscan,	NULL	},
172 	{ "-iscan",	P_CLR,	&opt_iscan,	NULL	},
173 	{ "switch",	P_SET,	&opt_switch,	NULL	},
174 	{ "-switch",	P_CLR,	&opt_switch,	NULL	},
175 	{ "hold",	P_SET,	&opt_hold,	NULL	},
176 	{ "-hold",	P_CLR,	&opt_hold,	NULL	},
177 	{ "sniff",	P_SET,	&opt_sniff,	NULL	},
178 	{ "-sniff",	P_CLR,	&opt_sniff,	NULL	},
179 	{ "park",	P_SET,	&opt_park,	NULL	},
180 	{ "-park",	P_CLR,	&opt_park,	NULL	},
181 	{ "auth",	P_SET,	&opt_auth,	NULL	},
182 	{ "-auth",	P_CLR,	&opt_auth,	NULL	},
183 	{ "encrypt",	P_SET,	&opt_encrypt,	NULL	},
184 	{ "-encrypt",	P_CLR,	&opt_encrypt,	NULL	},
185 	{ "ptype",	P_HEX,	&opt_ptype,	&ptype	},
186 	{ "class",	P_HEX,	&opt_class,	&class	},
187 	{ "fixed",	P_SET,	&opt_pin,	NULL	},
188 	{ "variable",	P_CLR,	&opt_pin,	NULL	},
189 	{ "inq",	P_SET,	&opt_inquiry,	NULL	},
190 	{ "inquiry",	P_SET,	&opt_inquiry,	NULL	},
191 	{ "rssi",	P_SET,	&opt_rssi,	NULL	},
192 	{ "-rssi",	P_CLR,	&opt_rssi,	NULL	},
193 	{ "reset",	P_SET,	&opt_reset,	NULL	},
194 	{ "voice",	P_HEX,	&opt_voice,	&voice	},
195 	{ "pto",	P_NUM,	&opt_pto,	&pto	},
196 	{ "scomtu",	P_NUM,	&opt_scomtu,	&scomtu	},
197 	{ NULL,		0,	NULL,		NULL	}
198 };
199 
200 int
main(int ac,char * av[])201 main(int ac, char *av[])
202 {
203 	int ch;
204 	struct parameter *p;
205 
206 	while ((ch = getopt(ac, av, "hlsvz")) != -1) {
207 		switch(ch) {
208 		case 'l':
209 			lflag = 1;
210 			break;
211 
212 		case 's':
213 			sflag = 1;
214 			break;
215 
216 		case 'v':
217 			verbose++;
218 			break;
219 
220 		case 'z':
221 			sflag = 2;
222 			break;
223 
224 		case 'h':
225 		default:
226 			usage();
227 		}
228 	}
229 	av += optind;
230 	ac -= optind;
231 
232 	if (lflag && sflag)
233 		usage();
234 
235 
236 	hci = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
237 	if (hci == -1)
238 		err(EXIT_FAILURE, "socket");
239 
240 	if (ac == 0) {
241 		verbose++;
242 
243 		memset(&btr, 0, sizeof(btr));
244 		while (set_unit(SIOCNBTINFO) != -1) {
245 			print_info(99);
246 			print_stats();
247 		}
248 
249 		tag(NULL);
250 	} else {
251 		strlcpy(btr.btr_name, *av, HCI_DEVNAME_SIZE);
252 		av++, ac--;
253 
254 		if (set_unit(SIOCGBTINFO) < 0)
255 			err(EXIT_FAILURE, "%s", btr.btr_name);
256 
257 		if (ac == 0)
258 			verbose += 2;
259 
260 		while (ac > 0) {
261 			for (p = parameters ; ; p++) {
262 				if (p->name == NULL)
263 					badparam(*av);
264 
265 				if (strcmp(*av, p->name) == 0)
266 					break;
267 			}
268 
269 			switch(p->type) {
270 			case P_SET:
271 				*(p->opt) = 1;
272 				break;
273 
274 			case P_CLR:
275 				*(p->opt) = -1;
276 				break;
277 
278 			case P_STR:
279 				if (--ac < 1) badarg(p->name);
280 				strlcpy((char *)(p->val), *++av, MAX_STR_SIZE);
281 				*(p->opt) = 1;
282 				break;
283 
284 			case P_HEX:
285 				if (--ac < 1) badarg(p->name);
286 				*(uint32_t *)(p->val) = strtoul(*++av, NULL, 16);
287 				*(p->opt) = 1;
288 				break;
289 
290 			case P_NUM:
291 				if (--ac < 1) badarg(p->name);
292 				*(uint32_t *)(p->val) = strtoul(*++av, NULL, 10);
293 				*(p->opt) = 1;
294 				break;
295 			}
296 
297 			av++, ac--;
298 		}
299 
300 		config_unit();
301 		print_info(verbose);
302 		print_stats();
303 		do_inquiry();
304 	}
305 
306 	close(hci);
307 	return EXIT_SUCCESS;
308 }
309 
310 static void
badparam(const char * param)311 badparam(const char *param)
312 {
313 	fprintf(stderr, "unknown parameter '%s'\n", param);
314 	exit(EXIT_FAILURE);
315 }
316 
317 static void
badarg(const char * param)318 badarg(const char *param)
319 {
320 	fprintf(stderr, "parameter '%s' needs argument\n", param);
321 	exit(EXIT_FAILURE);
322 }
323 
324 static void
usage(void)325 usage(void)
326 {
327 	fprintf(stderr, "usage:	%s [-svz] [device [parameters]]\n", getprogname());
328 	fprintf(stderr, "	%s -l\n", getprogname());
329 	exit(EXIT_FAILURE);
330 }
331 
332 /*
333  * pretty printing feature
334  */
335 static void
tag(const char * f)336 tag(const char *f)
337 {
338 	if (f == NULL) {
339 		if (width > 0)
340 			printf("\n");
341 
342 		width = 0;
343 	} else {
344 		width += printf("%*s%s",
345 				(width == 0 ? (lflag ? 0 : 8) : 1),
346 				"", f);
347 
348 		if (width > MAX_WIDTH) {
349 			printf("\n");
350 			width = 0;
351 		}
352 	}
353 }
354 
355 /*
356  * basic HCI cmd request function with argument return.
357  *
358  * Normally, this will return on COMMAND_STATUS or COMMAND_COMPLETE for the given
359  * opcode, but if event is given then it will ignore COMMAND_STATUS (unless error)
360  * and wait for the specified event.
361  *
362  * if rbuf/rlen is given, results will be copied into the result buffer for
363  * COMMAND_COMPLETE/event responses.
364  */
365 static void
hci_req(uint16_t opcode,uint8_t event,void * cbuf,size_t clen,void * rbuf,size_t rlen)366 hci_req(uint16_t opcode, uint8_t event, void *cbuf, size_t clen, void *rbuf, size_t rlen)
367 {
368 	uint8_t msg[sizeof(hci_cmd_hdr_t) + HCI_CMD_PKT_SIZE];
369 	hci_event_hdr_t *ep;
370 	hci_cmd_hdr_t *cp;
371 
372 	cp = (hci_cmd_hdr_t *)msg;
373 	cp->type = HCI_CMD_PKT;
374 	cp->opcode = opcode = htole16(opcode);
375 	cp->length = clen = MIN(clen, sizeof(msg) - sizeof(hci_cmd_hdr_t));
376 
377 	if (clen) memcpy((cp + 1), cbuf, clen);
378 
379 	if (send(hci, msg, sizeof(hci_cmd_hdr_t) + clen, 0) < 0)
380 		err(EXIT_FAILURE, "HCI Send");
381 
382 	ep = (hci_event_hdr_t *)msg;
383 	for(;;) {
384 		if (recv(hci, msg, sizeof(msg), 0) < 0) {
385 			if (errno == EAGAIN || errno == EINTR)
386 				continue;
387 
388 			err(EXIT_FAILURE, "HCI Recv");
389 		}
390 
391 		if (ep->event == HCI_EVENT_COMMAND_STATUS) {
392 			hci_command_status_ep *cs;
393 
394 			cs = (hci_command_status_ep *)(ep + 1);
395 			if (cs->opcode != opcode)
396 				continue;
397 
398 			if (cs->status)
399 				errx(EXIT_FAILURE,
400 				    "HCI cmd (%4.4x) failed (status %d)",
401 				    opcode, cs->status);
402 
403 			if (event == 0)
404 				break;
405 
406 			continue;
407 		}
408 
409 		if (ep->event == HCI_EVENT_COMMAND_COMPL) {
410 			hci_command_compl_ep *cc;
411 			uint8_t *ptr;
412 
413 			cc = (hci_command_compl_ep *)(ep + 1);
414 			if (cc->opcode != opcode)
415 				continue;
416 
417 			if (rbuf == NULL)
418 				break;
419 
420 			ptr = (uint8_t *)(cc + 1);
421 			if (*ptr)
422 				errx(EXIT_FAILURE,
423 				    "HCI cmd (%4.4x) failed (status %d)",
424 				    opcode, *ptr);
425 
426 			memcpy(rbuf, ++ptr, rlen);
427 			break;
428 		}
429 
430 		if (ep->event == event) {
431 			if (rbuf == NULL)
432 				break;
433 
434 			memcpy(rbuf, (ep + 1), rlen);
435 			break;
436 		}
437 	}
438 }
439 
440 static int
set_unit(unsigned long cmd)441 set_unit(unsigned long cmd)
442 {
443 	if (ioctl(hci, cmd, &btr) == -1)
444 		return -1;
445 
446 	if (btr.btr_flags & BTF_UP) {
447 		struct sockaddr_bt sa;
448 
449 		sa.bt_len = sizeof(sa);
450 		sa.bt_family = AF_BLUETOOTH;
451 		bdaddr_copy(&sa.bt_bdaddr, &btr.btr_bdaddr);
452 
453 		if (bind(hci, (struct sockaddr *)&sa, sizeof(sa)) < 0)
454 			err(EXIT_FAILURE, "bind");
455 
456 		if (connect(hci, (struct sockaddr *)&sa, sizeof(sa)) < 0)
457 			err(EXIT_FAILURE, "connect");
458 	}
459 
460 	return 0;
461 }
462 
463 /*
464  * apply configuration parameters to unit
465  */
466 static void
config_unit(void)467 config_unit(void)
468 {
469 	if (opt_enable) {
470 		if (opt_enable > 0)
471 			btr.btr_flags |= BTF_UP;
472 		else
473 			btr.btr_flags &= ~BTF_UP;
474 
475 		if (ioctl(hci, SIOCSBTFLAGS, &btr) < 0)
476 			err(EXIT_FAILURE, "SIOCSBTFLAGS");
477 
478 		if (set_unit(SIOCGBTINFO) < 0)
479 			err(EXIT_FAILURE, "%s", btr.btr_name);
480 	}
481 
482 	if (opt_reset) {
483 		hci_cmd(HCI_CMD_RESET, NULL, 0);
484 
485 		btr.btr_flags |= BTF_INIT;
486 		if (ioctl(hci, SIOCSBTFLAGS, &btr) < 0)
487 			err(EXIT_FAILURE, "SIOCSBTFLAGS");
488 
489 		/*
490 		 * although the reset command will automatically
491 		 * carry out these commands, we do them manually
492 		 * just so we can wait for completion.
493 		 */
494 		hci_cmd(HCI_CMD_READ_BDADDR, NULL, 0);
495 		hci_cmd(HCI_CMD_READ_BUFFER_SIZE, NULL, 0);
496 		hci_cmd(HCI_CMD_READ_LOCAL_FEATURES, NULL, 0);
497 
498 		if (set_unit(SIOCGBTINFO) < 0)
499 			err(EXIT_FAILURE, "%s", btr.btr_name);
500 	}
501 
502 	if (opt_switch || opt_hold || opt_sniff || opt_park) {
503 		uint16_t val = btr.btr_link_policy;
504 
505 		if (opt_switch > 0) val |= HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
506 		if (opt_switch < 0) val &= ~HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
507 		if (opt_hold > 0)   val |= HCI_LINK_POLICY_ENABLE_HOLD_MODE;
508 		if (opt_hold < 0)   val &= ~HCI_LINK_POLICY_ENABLE_HOLD_MODE;
509 		if (opt_sniff > 0)  val |= HCI_LINK_POLICY_ENABLE_SNIFF_MODE;
510 		if (opt_sniff < 0)  val &= ~HCI_LINK_POLICY_ENABLE_SNIFF_MODE;
511 		if (opt_park > 0)   val |= HCI_LINK_POLICY_ENABLE_PARK_MODE;
512 		if (opt_park < 0)   val &= ~HCI_LINK_POLICY_ENABLE_PARK_MODE;
513 
514 		btr.btr_link_policy = val;
515 		if (ioctl(hci, SIOCSBTPOLICY, &btr) < 0)
516 			err(EXIT_FAILURE, "SIOCSBTPOLICY");
517 	}
518 
519 	if (opt_ptype) {
520 		btr.btr_packet_type = ptype;
521 		if (ioctl(hci, SIOCSBTPTYPE, &btr) < 0)
522 			err(EXIT_FAILURE, "SIOCSBTPTYPE");
523 	}
524 
525 	if (opt_pscan || opt_iscan) {
526 		uint8_t val;
527 
528 		load_value(HCI_CMD_READ_SCAN_ENABLE, &val, sizeof(val));
529 		if (opt_pscan > 0) val |= HCI_PAGE_SCAN_ENABLE;
530 		if (opt_pscan < 0) val &= ~HCI_PAGE_SCAN_ENABLE;
531 		if (opt_iscan > 0) val |= HCI_INQUIRY_SCAN_ENABLE;
532 		if (opt_iscan < 0) val &= ~HCI_INQUIRY_SCAN_ENABLE;
533 		save_value(HCI_CMD_WRITE_SCAN_ENABLE, &val, sizeof(val));
534 	}
535 
536 	if (opt_auth) {
537 		uint8_t val = (opt_auth > 0 ? 1 : 0);
538 
539 		save_value(HCI_CMD_WRITE_AUTH_ENABLE, &val, sizeof(val));
540 	}
541 
542 	if (opt_encrypt) {
543 		uint8_t val = (opt_encrypt > 0 ? 1 : 0);
544 
545 		save_value(HCI_CMD_WRITE_ENCRYPTION_MODE, &val, sizeof(val));
546 	}
547 
548 	if (opt_name)
549 		save_value(HCI_CMD_WRITE_LOCAL_NAME, name, HCI_UNIT_NAME_SIZE);
550 
551 	if (opt_class) {
552 		uint8_t val[HCI_CLASS_SIZE];
553 
554 		val[0] = (class >> 0) & 0xff;
555 		val[1] = (class >> 8) & 0xff;
556 		val[2] = (class >> 16) & 0xff;
557 
558 		save_value(HCI_CMD_WRITE_UNIT_CLASS, val, HCI_CLASS_SIZE);
559 	}
560 
561 	if (opt_pin) {
562 		uint8_t val;
563 
564 		if (opt_pin > 0)	val = 1;
565 		else			val = 0;
566 
567 		save_value(HCI_CMD_WRITE_PIN_TYPE, &val, sizeof(val));
568 	}
569 
570 	if (opt_voice) {
571 		uint16_t val;
572 
573 		val = htole16(voice & 0x03ff);
574 		save_value(HCI_CMD_WRITE_VOICE_SETTING, &val, sizeof(val));
575 	}
576 
577 	if (opt_pto) {
578 		uint16_t val;
579 
580 		val = htole16(pto * 8 / 5);
581 		save_value(HCI_CMD_WRITE_PAGE_TIMEOUT, &val, sizeof(val));
582 	}
583 
584 	if (opt_scomtu) {
585 		if (scomtu > 0xff) {
586 			warnx("Invalid SCO mtu %d", scomtu);
587 		} else {
588 			btr.btr_sco_mtu = scomtu;
589 
590 			if (ioctl(hci, SIOCSBTSCOMTU, &btr) < 0)
591 				warn("SIOCSBTSCOMTU");
592 		}
593 	}
594 
595 	if (opt_rssi) {
596 		uint8_t val = (opt_rssi > 0 ? 1 : 0);
597 
598 		save_value(HCI_CMD_WRITE_INQUIRY_MODE, &val, sizeof(val));
599 	}
600 }
601 
602 /*
603  * Print info for Bluetooth Device with varying verbosity levels
604  */
605 static void
print_info(int level)606 print_info(int level)
607 {
608 	uint8_t val, buf[MAX_STR_SIZE];
609 	uint16_t val16;
610 
611 	if (lflag) {
612 		tag(btr.btr_name);
613 		return;
614 	}
615 
616 	if (level-- < 1)
617 		return;
618 
619 	printf("%s: bdaddr %s flags %#x", btr.btr_name,
620 		bt_ntoa(&btr.btr_bdaddr, NULL), btr.btr_flags);
621 	printb(btr.btr_flags, FLAGBITS);
622 	printf("\n");
623 
624 	if (level-- < 1)
625 		return;
626 
627 	printf("\tnum_cmd = %d\n"
628 	       "\tnum_acl = %d, acl_mtu = %d\n"
629 	       "\tnum_sco = %d, sco_mtu = %d\n",
630 	       btr.btr_num_cmd,
631 	       btr.btr_num_acl, btr.btr_acl_mtu,
632 	       btr.btr_num_sco, btr.btr_sco_mtu);
633 
634 	if (level-- < 1 || (btr.btr_flags & BTF_UP) == 0)
635 		return;
636 
637 	load_value(HCI_CMD_READ_UNIT_CLASS, buf, HCI_CLASS_SIZE);
638 	class = (buf[2] << 16) | (buf[1] << 8) | (buf[0]);
639 	print_class("\t");
640 
641 	load_value(HCI_CMD_READ_LOCAL_NAME, buf, HCI_UNIT_NAME_SIZE);
642 	printf("\tname: \"%s\"\n", buf);
643 
644 	load_value(HCI_CMD_READ_VOICE_SETTING, buf, sizeof(uint16_t));
645 	voice = (buf[1] << 8) | buf[0];
646 	print_voice(level);
647 
648 	load_value(HCI_CMD_READ_PIN_TYPE, &val, sizeof(val));
649 	printf("\tpin: %s\n", val ? "fixed" : "variable");
650 
651 	width = printf("\toptions:");
652 
653 	load_value(HCI_CMD_READ_SCAN_ENABLE, &val, sizeof(val));
654 	if (val & HCI_INQUIRY_SCAN_ENABLE)	tag("iscan");
655 	else if (level > 0)			tag("-iscan");
656 
657 	if (val & HCI_PAGE_SCAN_ENABLE)		tag("pscan");
658 	else if (level > 0)			tag("-pscan");
659 
660 	load_value(HCI_CMD_READ_AUTH_ENABLE, &val, sizeof(val));
661 	if (val)				tag("auth");
662 	else if (level > 0)			tag("-auth");
663 
664 	load_value(HCI_CMD_READ_ENCRYPTION_MODE, &val, sizeof(val));
665 	if (val)				tag("encrypt");
666 	else if (level > 0)			tag("-encrypt");
667 
668 	val = btr.btr_link_policy;
669 	if (val & HCI_LINK_POLICY_ENABLE_ROLE_SWITCH)	tag("switch");
670 	else if (level > 0)				tag("-switch");
671 	if (val & HCI_LINK_POLICY_ENABLE_HOLD_MODE)	tag("hold");
672 	else if (level > 0)				tag("-hold");
673 	if (val & HCI_LINK_POLICY_ENABLE_SNIFF_MODE)	tag("sniff");
674 	else if (level > 0)				tag("-sniff");
675 	if (val & HCI_LINK_POLICY_ENABLE_PARK_MODE)	tag("park");
676 	else if (level > 0)				tag("-park");
677 
678 	load_value(HCI_CMD_READ_INQUIRY_MODE, &val, sizeof(val));
679 	if (val)				tag("rssi");
680 	else if (level > 0)			tag("-rssi");
681 
682 	tag(NULL);
683 
684 	if (level-- < 1)
685 		return;
686 
687 	ptype = btr.btr_packet_type;
688 	width = printf("\tptype: [0x%04x]", ptype);
689 	if (ptype & HCI_PKT_DM1)		tag("DM1");
690 	if (ptype & HCI_PKT_DH1)		tag("DH1");
691 	if (ptype & HCI_PKT_DM3)		tag("DM3");
692 	if (ptype & HCI_PKT_DH3)		tag("DH3");
693 	if (ptype & HCI_PKT_DM5)		tag("DM5");
694 	if (ptype & HCI_PKT_DH5)		tag("DH5");
695 	if ((ptype & HCI_PKT_2MBPS_DH1) == 0)	tag("2-DH1");
696 	if ((ptype & HCI_PKT_3MBPS_DH1) == 0)	tag("3-DH1");
697 	if ((ptype & HCI_PKT_2MBPS_DH3) == 0)	tag("2-DH3");
698 	if ((ptype & HCI_PKT_3MBPS_DH3) == 0)	tag("3-DH3");
699 	if ((ptype & HCI_PKT_2MBPS_DH5) == 0)	tag("2-DH5");
700 	if ((ptype & HCI_PKT_3MBPS_DH5) == 0)	tag("3-DH5");
701 	tag(NULL);
702 
703 	load_value(HCI_CMD_READ_PAGE_TIMEOUT, &val16, sizeof(val16));
704 	printf("\tpage timeout: %d ms\n", val16 * 5 / 8);
705 
706 	if (level-- < 1)
707 		return;
708 
709 	load_value(HCI_CMD_READ_LOCAL_FEATURES, buf, HCI_FEATURES_SIZE);
710 	print_features("\tfeatures:", buf);
711 }
712 
713 static void
print_stats(void)714 print_stats(void)
715 {
716 	if (sflag == 0)
717 		return;
718 
719 	if (sflag == 1) {
720 		if (ioctl(hci, SIOCGBTSTATS, &btr) < 0)
721 			err(EXIT_FAILURE, "SIOCGBTSTATS");
722 	} else  {
723 		if (ioctl(hci, SIOCZBTSTATS, &btr) < 0)
724 			err(EXIT_FAILURE, "SIOCZBTSTATS");
725 	}
726 
727 	printf( "\tTotal bytes sent %d, received %d\n"
728 		"\tCommands sent %d, Events received %d\n"
729 		"\tACL data packets sent %d, received %d\n"
730 		"\tSCO data packets sent %d, received %d\n"
731 		"\tInput errors %d, Output errors %d\n",
732 		btr.btr_stats.byte_tx, btr.btr_stats.byte_rx,
733 		btr.btr_stats.cmd_tx, btr.btr_stats.evt_rx,
734 		btr.btr_stats.acl_tx, btr.btr_stats.acl_rx,
735 		btr.btr_stats.sco_tx, btr.btr_stats.sco_rx,
736 		btr.btr_stats.err_rx, btr.btr_stats.err_tx);
737 }
738 
739 static void
print_features(const char * str,uint8_t * f)740 print_features(const char *str, uint8_t *f)
741 {
742 	width = printf("%s", str);
743 
744 	/* ------------------- byte 0 --------------------*/
745 	if (*f & HCI_LMP_3SLOT)		    tag("<3 slot>");
746 	if (*f & HCI_LMP_5SLOT)		    tag("<5 slot>");
747 	if (*f & HCI_LMP_ENCRYPTION)	    tag("<encryption>");
748 	if (*f & HCI_LMP_SLOT_OFFSET)	    tag("<slot offset>");
749 	if (*f & HCI_LMP_TIMIACCURACY)	    tag("<timing accuracy>");
750 	if (*f & HCI_LMP_ROLE_SWITCH)	    tag("<role switch>");
751 	if (*f & HCI_LMP_HOLD_MODE)	    tag("<hold mode>");
752 	if (*f & HCI_LMP_SNIFF_MODE)	    tag("<sniff mode>");
753 	f++;
754 
755 	/* ------------------- byte 1 --------------------*/
756 	if (*f & HCI_LMP_PARK_MODE)	    tag("<park mode>");
757 	if (*f & HCI_LMP_RSSI)		    tag("<RSSI>");
758 	if (*f & HCI_LMP_CHANNEL_QUALITY)   tag("<channel quality>");
759 	if (*f & HCI_LMP_SCO_LINK)	    tag("<SCO link>");
760 	if (*f & HCI_LMP_HV2_PKT)	    tag("<HV2>");
761 	if (*f & HCI_LMP_HV3_PKT)	    tag("<HV3>");
762 	if (*f & HCI_LMP_ULAW_LOG)	    tag("<u-Law log>");
763 	if (*f & HCI_LMP_ALAW_LOG)	    tag("<A-Law log>");
764 	f++;
765 
766 	/* ------------------- byte 1 --------------------*/
767 	if (*f & HCI_LMP_CVSD)		    tag("<CVSD data>");
768 	if (*f & HCI_LMP_PAGISCHEME)	    tag("<paging parameter>");
769 	if (*f & HCI_LMP_POWER_CONTROL)	    tag("<power control>");
770 	if (*f & HCI_LMP_TRANSPARENT_SCO)   tag("<transparent SCO>");
771 	if (*f & HCI_LMP_FLOW_CONTROL_LAG0) tag("<flow control lag 0>");
772 	if (*f & HCI_LMP_FLOW_CONTROL_LAG1) tag("<flow control lag 1>");
773 	if (*f & HCI_LMP_FLOW_CONTROL_LAG2) tag("<flow control lag 2>");
774 	if (*f & HCI_LMP_BC_ENCRYPTION)	    tag("<broadcast encryption>");
775 	f++;
776 
777 	/* ------------------- byte 3 --------------------*/
778 	if (*f & HCI_LMP_EDR_ACL_2MBPS)	    tag("<EDR ACL 2Mbps>");
779 	if (*f & HCI_LMP_EDR_ACL_3MBPS)	    tag("<EDR ACL 3Mbps>");
780 	if (*f & HCI_LMP_ENHANCED_ISCAN)    tag("<enhanced inquiry scan>");
781 	if (*f & HCI_LMP_INTERLACED_ISCAN)  tag("<interlaced inquiry scan>");
782 	if (*f & HCI_LMP_INTERLACED_PSCAN)  tag("<interlaced page scan>");
783 	if (*f & HCI_LMP_RSSI_INQUIRY)	    tag("<RSSI with inquiry result>");
784 	if (*f & HCI_LMP_EV3_PKT)	    tag("<EV3 packets>");
785 	f++;
786 
787 	/* ------------------- byte 4 --------------------*/
788 	if (*f & HCI_LMP_EV4_PKT)	    tag("<EV4 packets>");
789 	if (*f & HCI_LMP_EV5_PKT)	    tag("<EV5 packets>");
790 	if (*f & HCI_LMP_AFH_CAPABLE_SLAVE) tag("<AFH capable slave>");
791 	if (*f & HCI_LMP_AFH_CLASS_SLAVE)   tag("<AFH class slave>");
792 	if (*f & HCI_LMP_3SLOT_EDR_ACL)	    tag("<3 slot EDR ACL>");
793 	f++;
794 
795 	/* ------------------- byte 5 --------------------*/
796 	if (*f & HCI_LMP_5SLOT_EDR_ACL)	    tag("<5 slot EDR ACL>");
797 	if (*f & HCI_LMP_AFH_CAPABLE_MASTER)tag("<AFH capable master>");
798 	if (*f & HCI_LMP_AFH_CLASS_MASTER)  tag("<AFH class master>");
799 	if (*f & HCI_LMP_EDR_eSCO_2MBPS)    tag("<EDR eSCO 2Mbps>");
800 	if (*f & HCI_LMP_EDR_eSCO_3MBPS)    tag("<EDR eSCO 3Mbps>");
801 	if (*f & HCI_LMP_3SLOT_EDR_eSCO)    tag("<3 slot EDR eSCO>");
802 	f++;
803 
804 	/* ------------------- byte 6 --------------------*/
805 	f++;
806 
807 	/* ------------------- byte 7 --------------------*/
808 	if (*f & HCI_LMP_EXTENDED_FEATURES) tag("<extended features>");
809 
810 	tag(NULL);
811 }
812 
813 static void
print_class(const char * str)814 print_class(const char *str)
815 {
816 	int major, minor;
817 
818 	major = (class & 0x1f00) >> 8;
819 	minor = (class & 0x00fc) >> 2;
820 
821 	width = printf("%sclass: [0x%6.6x]", str, class);
822 
823 	switch (major) {
824 	case 1:	/* Computer */
825 		switch (minor) {
826 		case 1: tag("Desktop");				break;
827 		case 2: tag("Server");				break;
828 		case 3: tag("Laptop");				break;
829 		case 4: tag("Handheld");			break;
830 		case 5: tag("Palm Sized");			break;
831 		case 6: tag("Wearable");			break;
832 		}
833 		tag("Computer");
834 		break;
835 
836 	case 2:	/* Phone */
837 		switch (minor) {
838 		case 1: tag("Cellular Phone");			break;
839 		case 2: tag("Cordless Phone");			break;
840 		case 3: tag("Smart Phone");			break;
841 		case 4: tag("Wired Modem/Phone Gateway");	break;
842 		case 5: tag("Common ISDN");			break;
843 		default:tag("Phone");				break;
844 		}
845 		break;
846 
847 	case 3:	/* LAN */
848 		tag("LAN");
849 		switch ((minor & 0x38) >> 3) {
850 		case 0: tag("[Fully available]");		break;
851 		case 1: tag("[1-17% utilised]");		break;
852 		case 2: tag("[17-33% utilised]");		break;
853 		case 3: tag("[33-50% utilised]");		break;
854 		case 4: tag("[50-67% utilised]");		break;
855 		case 5: tag("[67-83% utilised]");		break;
856 		case 6: tag("[83-99% utilised]");		break;
857 		case 7: tag("[No service available]");		break;
858 		}
859 		break;
860 
861 	case 4:	/* Audio/Visual */
862 		switch (minor) {
863 		case 1: tag("Wearable Headset");		break;
864 		case 2: tag("Hands-free Audio");		break;
865 		case 4: tag("Microphone");			break;
866 		case 5: tag("Loudspeaker");			break;
867 		case 6: tag("Headphones");			break;
868 		case 7: tag("Portable Audio");			break;
869 		case 8: tag("Car Audio");			break;
870 		case 9: tag("Set-top Box");			break;
871 		case 10: tag("HiFi Audio");			break;
872 		case 11: tag("VCR");				break;
873 		case 12: tag("Video Camera");			break;
874 		case 13: tag("Camcorder");			break;
875 		case 14: tag("Video Monitor");			break;
876 		case 15: tag("Video Display and Loudspeaker");	break;
877 		case 16: tag("Video Conferencing");		break;
878 		case 18: tag("A/V [Gaming/Toy]");		break;
879 		default: tag("Audio/Visual");			break;
880 		}
881 		break;
882 
883 	case 5:	/* Peripheral */
884 		switch (minor & 0x0f) {
885 		case 1: tag("Joystick");			break;
886 		case 2: tag("Gamepad");				break;
887 		case 3: tag("Remote Control");			break;
888 		case 4: tag("Sensing Device");			break;
889 		case 5: tag("Digitiser Tablet");		break;
890 		case 6: tag("Card Reader");			break;
891 		default: tag("Peripheral");			break;
892 		}
893 
894 		if (minor & 0x10) tag("Keyboard");
895 		if (minor & 0x20) tag("Mouse");
896 		break;
897 
898 	case 6:	/* Imaging */
899 		if (minor & 0x20) tag("Printer");
900 		if (minor & 0x10) tag("Scanner");
901 		if (minor & 0x08) tag("Camera");
902 		if (minor & 0x04) tag("Display");
903 		if ((minor & 0x3c) == 0) tag("Imaging");
904 		break;
905 
906 	case 7:	/* Wearable */
907 		switch (minor) {
908 		case 1: tag("Wrist Watch");			break;
909 		case 2: tag("Pager");				break;
910 		case 3: tag("Jacket");				break;
911 		case 4: tag("Helmet");				break;
912 		case 5: tag("Glasses");				break;
913 		default: tag("Wearable");			break;
914 		}
915 		break;
916 
917 	case 8:	/* Toy */
918 		switch (minor) {
919 		case 1: tag("Robot");				break;
920 		case 2: tag("Vehicle");				break;
921 		case 3: tag("Doll / Action Figure");		break;
922 		case 4: tag("Controller");			break;
923 		case 5: tag("Game");				break;
924 		default: tag("Toy");				break;
925 		}
926 		break;
927 
928 	default:
929 		break;
930 	}
931 
932 	if (class & 0x002000)	tag("<Limited Discoverable>");
933 	if (class & 0x010000)	tag("<Positioning>");
934 	if (class & 0x020000)	tag("<Networking>");
935 	if (class & 0x040000)	tag("<Rendering>");
936 	if (class & 0x080000)	tag("<Capturing>");
937 	if (class & 0x100000)	tag("<Object Transfer>");
938 	if (class & 0x200000)	tag("<Audio>");
939 	if (class & 0x400000)	tag("<Telephony>");
940 	if (class & 0x800000)	tag("<Information>");
941 	tag(NULL);
942 }
943 
944 static void
print_voice(int level)945 print_voice(int level)
946 {
947 	printf("\tvoice: [0x%4.4x]\n", voice);
948 
949 	if (level == 0)
950 		return;
951 
952 	printf("\t\tInput Coding: ");
953 	switch ((voice & 0x0300) >> 8) {
954 	case 0x00:	printf("Linear PCM [%d-bit, pos %d]",
955 			(voice & 0x0020 ? 16 : 8),
956 			(voice & 0x001c) >> 2);		break;
957 	case 0x01:	printf("u-Law");		break;
958 	case 0x02:	printf("A-Law");		break;
959 	case 0x03:	printf("unknown");		break;
960 	}
961 
962 	switch ((voice & 0x00c0) >> 6) {
963 	case 0x00:	printf(", 1's complement");	break;
964 	case 0x01:	printf(", 2's complement");	break;
965 	case 0x02:	printf(", sign magnitude");	break;
966 	case 0x03:	printf(", unsigned");		break;
967 	}
968 
969 	printf("\n\t\tAir Coding: ");
970 	switch (voice & 0x0003) {
971 	case 0x00:	printf("CVSD");			break;
972 	case 0x01:	printf("u-Law");		break;
973 	case 0x02:	printf("A-Law");		break;
974 	case 0x03:	printf("Transparent");		break;
975 	}
976 
977 	printf("\n");
978 }
979 
980 static void
print_result(int num,struct result * r,int rssi)981 print_result(int num, struct result *r, int rssi)
982 {
983 	hci_remote_name_req_cp ncp;
984 	hci_remote_name_req_compl_ep nep;
985 #if 0
986 	hci_read_remote_features_cp fcp;
987 	hci_read_remote_features_compl_ep fep;
988 	struct hostent *hp;
989 #endif
990 
991 	printf("%3d: bdaddr %s",
992 			num,
993 			bt_ntoa(&r->bdaddr, NULL));
994 
995 #if 0
996 	hp = bt_gethostbyaddr(&r->bdaddr, sizeof(bdaddr_t), AF_BLUETOOTH);
997 	if (hp != NULL)
998 		printf(" (%s)", hp->h_name);
999 #endif
1000 	printf("\n");
1001 
1002 	memset(&ncp, 0, sizeof(ncp));
1003 	bdaddr_copy(&ncp.bdaddr, &r->bdaddr);
1004 	ncp.page_scan_rep_mode = r->page_scan_rep_mode;
1005 	ncp.clock_offset = r->clock_offset;
1006 
1007 	hci_req(HCI_CMD_REMOTE_NAME_REQ,
1008 		HCI_EVENT_REMOTE_NAME_REQ_COMPL,
1009 		&ncp, sizeof(ncp),
1010 		&nep, sizeof(nep));
1011 
1012 	printf("   : name \"%s\"\n", nep.name);
1013 
1014 	class = (r->uclass[2] << 16) | (r->uclass[1] << 8) | (r->uclass[0]);
1015 	print_class("   : ");
1016 
1017 #if 0
1018 	hci_req(HCI_CMD_READ_REMOTE_FEATURES,
1019 		HCI_EVENT_READ_REMOTE_FEATURES_COMPL,
1020 		&fcp, sizeof(fcp),
1021 		&fep, sizeof(fep));
1022 
1023 	print_features("   : features", fep.features);
1024 #endif
1025 
1026 	printf("   : page scan rep mode 0x%02x\n", r->page_scan_rep_mode);
1027 	printf("   : clock offset %d\n", le16toh(r->clock_offset));
1028 
1029 	if (rssi)
1030 		printf("   : rssi %d\n", r->rssi);
1031 
1032 	printf("\n");
1033 }
1034 
1035 static void
do_inquiry(void)1036 do_inquiry(void)
1037 {
1038 	uint8_t buf[HCI_EVENT_PKT_SIZE];
1039 	struct result result[INQUIRY_MAX_RESPONSES];
1040 	hci_inquiry_cp inq;
1041 	struct hci_filter f;
1042 	hci_event_hdr_t *hh;
1043 	int i, j, num, rssi;
1044 
1045 	if (opt_inquiry == 0)
1046 		return;
1047 
1048 	printf("Device Discovery from device: %s ...", btr.btr_name);
1049 	fflush(stdout);
1050 
1051 	memset(&f, 0, sizeof(f));
1052 	hci_filter_set(HCI_EVENT_COMMAND_STATUS, &f);
1053 	hci_filter_set(HCI_EVENT_COMMAND_COMPL, &f);
1054 	hci_filter_set(HCI_EVENT_INQUIRY_RESULT, &f);
1055 	hci_filter_set(HCI_EVENT_RSSI_RESULT, &f);
1056 	hci_filter_set(HCI_EVENT_INQUIRY_COMPL, &f);
1057 	hci_filter_set(HCI_EVENT_REMOTE_NAME_REQ_COMPL, &f);
1058 	hci_filter_set(HCI_EVENT_READ_REMOTE_FEATURES_COMPL, &f);
1059 	if (setsockopt(hci, BTPROTO_HCI, SO_HCI_EVT_FILTER, &f, sizeof(f)) < 0)
1060 		err(EXIT_FAILURE, "Can't set event filter");
1061 
1062 	/* General Inquiry LAP is 0x9e8b33 */
1063 	inq.lap[0] = 0x33;
1064 	inq.lap[1] = 0x8b;
1065 	inq.lap[2] = 0x9e;
1066 	inq.inquiry_length = INQUIRY_LENGTH;
1067 	inq.num_responses = INQUIRY_MAX_RESPONSES;
1068 
1069 	hci_cmd(HCI_CMD_INQUIRY, &inq, sizeof(inq));
1070 
1071 	num = 0;
1072 	rssi = 0;
1073 	hh = (hci_event_hdr_t *)buf;
1074 
1075 	for (;;) {
1076 		if (recv(hci, buf, sizeof(buf), 0) <= 0)
1077 			err(EXIT_FAILURE, "recv");
1078 
1079 		if (hh->event == HCI_EVENT_INQUIRY_COMPL)
1080 			break;
1081 
1082 		if (hh->event == HCI_EVENT_INQUIRY_RESULT) {
1083 			hci_inquiry_result_ep *ep = (hci_inquiry_result_ep *)(hh + 1);
1084 			hci_inquiry_response *ir = (hci_inquiry_response *)(ep + 1);
1085 
1086 			for (i = 0 ; i < ep->num_responses ; i++) {
1087 				if (num == INQUIRY_MAX_RESPONSES)
1088 					break;
1089 
1090 				/* some devices keep responding, ignore dupes */
1091 				for (j = 0 ; j < num ; j++)
1092 					if (bdaddr_same(&result[j].bdaddr, &ir[i].bdaddr))
1093 						break;
1094 
1095 				if (j < num)
1096 					continue;
1097 
1098 				bdaddr_copy(&result[num].bdaddr, &ir[i].bdaddr);
1099 				memcpy(&result[num].uclass, &ir[i].uclass, HCI_CLASS_SIZE);
1100 				result[num].page_scan_rep_mode = ir[i].page_scan_rep_mode;
1101 				result[num].clock_offset = ir[i].clock_offset;
1102 				result[num].rssi = 0;
1103 				num++;
1104 				printf(".");
1105 				fflush(stdout);
1106 			}
1107 			continue;
1108 		}
1109 #if 0
1110 		if (hh->event == HCI_EVENT_RSSI_RESULT) {
1111 			hci_rssi_result_ep *ep = (hci_rssi_result_ep *)(hh + 1);
1112 			hci_rssi_response *rr = (hci_rssi_response *)(ep + 1);
1113 
1114 			for (i = 0 ; i < ep->num_responses ; i++) {
1115 				if (num == INQUIRY_MAX_RESPONSES)
1116 					break;
1117 
1118 				/* some devices keep responding, ignore dupes */
1119 				for (j = 0 ; j < num ; j++)
1120 					if (bdaddr_same(&result[j].bdaddr, &rr[i].bdaddr))
1121 						break;
1122 
1123 				if (j < num)
1124 					continue;
1125 
1126 				bdaddr_copy(&result[num].bdaddr, &rr[i].bdaddr);
1127 				memcpy(&result[num].uclass, &rr[i].uclass, HCI_CLASS_SIZE);
1128 				result[num].page_scan_rep_mode = rr[i].page_scan_rep_mode;
1129 				result[num].clock_offset = rr[i].clock_offset;
1130 				result[num].rssi = rr[i].rssi;
1131 				rssi = 1;
1132 				num++;
1133 				printf(".");
1134 				fflush(stdout);
1135 			}
1136 			continue;
1137 		}
1138 #endif
1139 	}
1140 
1141 	printf(" %d response%s\n", num, (num == 1 ? "" : "s"));
1142 
1143 	for (i = 0 ; i < num ; i++)
1144 		print_result(i + 1, &result[i], rssi);
1145 }
1146 
1147 /*
1148  * Print a value a la the %pb%i format of the kernel's kprintf() borrowed
1149  * from ifconfig(8).
1150  */
1151 static void
printb(uint16_t v,const char * bits)1152 printb(uint16_t v, const char *bits)
1153 {
1154 	int i, any = 0;
1155 	char c;
1156 
1157 	if (bits) {
1158 		putchar('<');
1159 		while ((i = *bits++) != '\0') {
1160 			if (v & (1 << (i-1))) {
1161 				if (any)
1162 					putchar(',');
1163 				any = 1;
1164 				for (; (c = *bits) > 32; bits++)
1165 					putchar(c);
1166 			} else
1167 				for (; *bits > 32; bits++)
1168 					;
1169 		}
1170 		putchar('>');
1171 	}
1172 }
1173 
1174