xref: /freebsd/usr.sbin/bluetooth/hccontrol/hccontrol.c (revision fdbf7cab91ae9ae7ca87bd47acb7400813bd7160)
11de7b4b8SPedro F. Giffuni /*-
2878ed226SJulian Elischer  * hccontrol.c
3878ed226SJulian Elischer  *
44d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
51de7b4b8SPedro F. Giffuni  *
6878ed226SJulian Elischer  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7878ed226SJulian Elischer  * All rights reserved.
8878ed226SJulian Elischer  *
9878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
10878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
11878ed226SJulian Elischer  * are met:
12878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
13878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
14878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
16878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
17878ed226SJulian Elischer  *
18878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28878ed226SJulian Elischer  * SUCH DAMAGE.
29878ed226SJulian Elischer  *
300986ab12SMaksim Yevmenkin  * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $
31878ed226SJulian Elischer  */
32878ed226SJulian Elischer 
338d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
340986ab12SMaksim Yevmenkin #include <bluetooth.h>
35fc5806edSMarkus Brueffer #include <sys/ioctl.h>
36*fdbf7cabSElyes Haouas #include <sys/param.h>
37878ed226SJulian Elischer #include <sys/sysctl.h>
38878ed226SJulian Elischer #include <assert.h>
39878ed226SJulian Elischer #include <err.h>
40878ed226SJulian Elischer #include <errno.h>
41fc5806edSMarkus Brueffer #include <netgraph/ng_message.h>
42878ed226SJulian Elischer #include <stdio.h>
43878ed226SJulian Elischer #include <stdlib.h>
44878ed226SJulian Elischer #include <string.h>
45878ed226SJulian Elischer #include <unistd.h>
46878ed226SJulian Elischer #include "hccontrol.h"
47878ed226SJulian Elischer 
48878ed226SJulian Elischer /* Prototypes */
49878ed226SJulian Elischer static int                  do_hci_command    (char const *, int, char **);
50878ed226SJulian Elischer static struct hci_command * find_hci_command  (char const *, struct hci_command *);
51fc5806edSMarkus Brueffer static int                  find_hci_nodes    (struct nodeinfo **);
52878ed226SJulian Elischer static void                 print_hci_command (struct hci_command *);
53878ed226SJulian Elischer static void usage                             (void);
54878ed226SJulian Elischer 
55878ed226SJulian Elischer /* Globals */
56878ed226SJulian Elischer int	 verbose = 0;
57878ed226SJulian Elischer int	 timeout;
580986ab12SMaksim Yevmenkin int	 numeric_bdaddr = 0;
59878ed226SJulian Elischer 
60878ed226SJulian Elischer /* Main */
61878ed226SJulian Elischer int
62878ed226SJulian Elischer main(int argc, char *argv[])
63878ed226SJulian Elischer {
64878ed226SJulian Elischer 	char	*node = NULL;
65878ed226SJulian Elischer 	int	 n;
66878ed226SJulian Elischer 
67878ed226SJulian Elischer 	/* Process command line arguments */
680986ab12SMaksim Yevmenkin 	while ((n = getopt(argc, argv, "n:Nvh")) != -1) {
69878ed226SJulian Elischer 		switch (n) {
70878ed226SJulian Elischer 		case 'n':
71878ed226SJulian Elischer 			node = optarg;
72878ed226SJulian Elischer 			break;
73878ed226SJulian Elischer 
740986ab12SMaksim Yevmenkin 		case 'N':
750986ab12SMaksim Yevmenkin 			numeric_bdaddr = 1;
760986ab12SMaksim Yevmenkin 			break;
770986ab12SMaksim Yevmenkin 
78878ed226SJulian Elischer 		case 'v':
79878ed226SJulian Elischer 			verbose = 1;
80878ed226SJulian Elischer 			break;
81878ed226SJulian Elischer 
821a63eb31SJulian Elischer 		case 'h':
83878ed226SJulian Elischer 		default:
84878ed226SJulian Elischer 			usage();
85878ed226SJulian Elischer 		}
86878ed226SJulian Elischer 	}
87878ed226SJulian Elischer 
88878ed226SJulian Elischer 	argc -= optind;
89878ed226SJulian Elischer 	argv += optind;
90878ed226SJulian Elischer 
91878ed226SJulian Elischer 	if (*argv == NULL)
92878ed226SJulian Elischer 		usage();
93878ed226SJulian Elischer 
94878ed226SJulian Elischer 	n = do_hci_command(node, argc, argv);
95878ed226SJulian Elischer 
96878ed226SJulian Elischer 	return (n);
97878ed226SJulian Elischer } /* main */
98878ed226SJulian Elischer 
99878ed226SJulian Elischer /* Create socket and bind it */
100878ed226SJulian Elischer static int
101878ed226SJulian Elischer socket_open(char const *node)
102878ed226SJulian Elischer {
103878ed226SJulian Elischer 	struct sockaddr_hci			 addr;
104878ed226SJulian Elischer 	struct ng_btsocket_hci_raw_filter	 filter;
10502afd3d1SMarkus Brueffer 	int					 s, mib[4], num;
106878ed226SJulian Elischer 	size_t					 size;
107fc5806edSMarkus Brueffer 	struct nodeinfo 			*nodes;
10863c1ff65SStephen J. Kiernan 	char                                    *lnode = NULL;
109878ed226SJulian Elischer 
11002afd3d1SMarkus Brueffer 	num = find_hci_nodes(&nodes);
11102afd3d1SMarkus Brueffer 	if (num == 0)
11202afd3d1SMarkus Brueffer 		errx(7, "Could not find HCI nodes");
113fc5806edSMarkus Brueffer 
114fc5806edSMarkus Brueffer 	if (node == NULL) {
11563c1ff65SStephen J. Kiernan 		node = lnode = strdup(nodes[0].name);
11602afd3d1SMarkus Brueffer 		if (num > 1)
117fc5806edSMarkus Brueffer 			fprintf(stdout, "Using HCI node: %s\n", node);
118fc5806edSMarkus Brueffer 	}
119fc5806edSMarkus Brueffer 
120fc5806edSMarkus Brueffer 	free(nodes);
121878ed226SJulian Elischer 
122878ed226SJulian Elischer 	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
123878ed226SJulian Elischer 	if (s < 0)
124878ed226SJulian Elischer 		err(1, "Could not create socket");
125878ed226SJulian Elischer 
126878ed226SJulian Elischer 	memset(&addr, 0, sizeof(addr));
127878ed226SJulian Elischer 	addr.hci_len = sizeof(addr);
128878ed226SJulian Elischer 	addr.hci_family = AF_BLUETOOTH;
129878ed226SJulian Elischer 	strncpy(addr.hci_node, node, sizeof(addr.hci_node));
130878ed226SJulian Elischer 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
131878ed226SJulian Elischer 		err(2, "Could not bind socket, node=%s", node);
132878ed226SJulian Elischer 
133878ed226SJulian Elischer 	if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
134878ed226SJulian Elischer 		err(3, "Could not connect socket, node=%s", node);
135878ed226SJulian Elischer 
13663c1ff65SStephen J. Kiernan 	free(lnode);
137878ed226SJulian Elischer 	memset(&filter, 0, sizeof(filter));
138878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1);
139878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1);
140878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1);
141878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1);
142878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1);
143878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1);
144878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1);
145878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1);
146878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1);
147878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1);
148878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1);
149878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1);
150878ed226SJulian Elischer 	bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1);
151bb9157d5STakanori Watanabe 	bit_set(filter.event_mask, NG_HCI_EVENT_LE -1);
152878ed226SJulian Elischer 
153878ed226SJulian Elischer 	if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
154878ed226SJulian Elischer 			(void * const) &filter, sizeof(filter)) < 0)
155878ed226SJulian Elischer 		err(4, "Could not setsockopt()");
156878ed226SJulian Elischer 
157*fdbf7cabSElyes Haouas 	size = nitems(mib);
158878ed226SJulian Elischer 	if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0)
159878ed226SJulian Elischer 		err(5, "Could not sysctlnametomib()");
160878ed226SJulian Elischer 
161*fdbf7cabSElyes Haouas 	if (sysctl(mib, nitems(mib),
162878ed226SJulian Elischer 			(void *) &timeout, &size, NULL, 0) < 0)
163878ed226SJulian Elischer 		err(6, "Could not sysctl()");
164878ed226SJulian Elischer 
165878ed226SJulian Elischer 	timeout ++;
166878ed226SJulian Elischer 
167878ed226SJulian Elischer 	return (s);
168878ed226SJulian Elischer } /* socket_open */
169878ed226SJulian Elischer 
170878ed226SJulian Elischer /* Execute commands */
171878ed226SJulian Elischer static int
172878ed226SJulian Elischer do_hci_command(char const *node, int argc, char **argv)
173878ed226SJulian Elischer {
174878ed226SJulian Elischer 	char			*cmd = argv[0];
175878ed226SJulian Elischer 	struct hci_command	*c = NULL;
176878ed226SJulian Elischer 	int			 s, e, help;
177878ed226SJulian Elischer 
178878ed226SJulian Elischer 	help = 0;
179878ed226SJulian Elischer 	if (strcasecmp(cmd, "help") == 0) {
180878ed226SJulian Elischer 		argc --;
181878ed226SJulian Elischer 		argv ++;
182878ed226SJulian Elischer 
183878ed226SJulian Elischer 		if (argc <= 0) {
184878ed226SJulian Elischer 			fprintf(stdout, "Supported commands:\n");
185878ed226SJulian Elischer 			print_hci_command(link_control_commands);
186878ed226SJulian Elischer 			print_hci_command(link_policy_commands);
187878ed226SJulian Elischer 			print_hci_command(host_controller_baseband_commands);
188878ed226SJulian Elischer 			print_hci_command(info_commands);
189878ed226SJulian Elischer 			print_hci_command(status_commands);
190bcff2d91STakanori Watanabe 			print_hci_command(le_commands);
191878ed226SJulian Elischer 			print_hci_command(node_commands);
192878ed226SJulian Elischer 			fprintf(stdout, "\nFor more information use " \
193878ed226SJulian Elischer 				"'help command'\n");
194878ed226SJulian Elischer 
195878ed226SJulian Elischer 			return (OK);
196878ed226SJulian Elischer 		}
197878ed226SJulian Elischer 
198878ed226SJulian Elischer 		help = 1;
199878ed226SJulian Elischer 		cmd = argv[0];
200878ed226SJulian Elischer 	}
201878ed226SJulian Elischer 
202878ed226SJulian Elischer 	c = find_hci_command(cmd, link_control_commands);
203878ed226SJulian Elischer 	if (c != NULL)
204878ed226SJulian Elischer 		goto execute;
205878ed226SJulian Elischer 
206878ed226SJulian Elischer 	c = find_hci_command(cmd, link_policy_commands);
207878ed226SJulian Elischer 	if (c != NULL)
208878ed226SJulian Elischer 		goto execute;
209878ed226SJulian Elischer 
210878ed226SJulian Elischer 	c = find_hci_command(cmd, host_controller_baseband_commands);
211878ed226SJulian Elischer 	if (c != NULL)
212878ed226SJulian Elischer 		goto execute;
213878ed226SJulian Elischer 
214878ed226SJulian Elischer 	c = find_hci_command(cmd, info_commands);
215878ed226SJulian Elischer 	if (c != NULL)
216878ed226SJulian Elischer 		goto execute;
217878ed226SJulian Elischer 
218878ed226SJulian Elischer 	c = find_hci_command(cmd, status_commands);
219878ed226SJulian Elischer 	if (c != NULL)
220878ed226SJulian Elischer 		goto execute;
221878ed226SJulian Elischer 
222bcff2d91STakanori Watanabe 	c = find_hci_command(cmd, le_commands);
223bcff2d91STakanori Watanabe 	if (c != NULL)
224bcff2d91STakanori Watanabe 		goto execute;
225bcff2d91STakanori Watanabe 
226bcff2d91STakanori Watanabe 
227878ed226SJulian Elischer 	c = find_hci_command(cmd, node_commands);
228878ed226SJulian Elischer 	if (c == NULL) {
229878ed226SJulian Elischer 		fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
230878ed226SJulian Elischer 		return (ERROR);
231878ed226SJulian Elischer 	}
232878ed226SJulian Elischer execute:
233878ed226SJulian Elischer 	if (!help) {
234878ed226SJulian Elischer 		s = socket_open(node);
235878ed226SJulian Elischer 		e = (c->handler)(s, -- argc, ++ argv);
236878ed226SJulian Elischer 		close(s);
237878ed226SJulian Elischer 	} else
238878ed226SJulian Elischer 		e = USAGE;
239878ed226SJulian Elischer 
240878ed226SJulian Elischer 	switch (e) {
241878ed226SJulian Elischer 	case OK:
242878ed226SJulian Elischer 	case FAILED:
243878ed226SJulian Elischer 		break;
244878ed226SJulian Elischer 
245878ed226SJulian Elischer 	case ERROR:
246878ed226SJulian Elischer 		fprintf(stdout, "Could not execute command \"%s\". %s\n",
247878ed226SJulian Elischer 			cmd, strerror(errno));
248878ed226SJulian Elischer 		break;
249878ed226SJulian Elischer 
250878ed226SJulian Elischer 	case USAGE:
251878ed226SJulian Elischer 		fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
252878ed226SJulian Elischer 		break;
253878ed226SJulian Elischer 
254878ed226SJulian Elischer 	default: assert(0); break;
255878ed226SJulian Elischer 	}
256878ed226SJulian Elischer 
257878ed226SJulian Elischer 
258878ed226SJulian Elischer 	return (e);
259878ed226SJulian Elischer } /* do_hci_command */
260878ed226SJulian Elischer 
261878ed226SJulian Elischer /* Try to find command in specified category */
262878ed226SJulian Elischer static struct hci_command *
263878ed226SJulian Elischer find_hci_command(char const *command, struct hci_command *category)
264878ed226SJulian Elischer {
265878ed226SJulian Elischer 	struct hci_command	*c = NULL;
266878ed226SJulian Elischer 
267878ed226SJulian Elischer 	for (c = category; c->command != NULL; c++) {
268878ed226SJulian Elischer 		char 	*c_end = strchr(c->command, ' ');
269878ed226SJulian Elischer 
270878ed226SJulian Elischer 		if (c_end != NULL) {
271878ed226SJulian Elischer 			int	len = c_end - c->command;
272878ed226SJulian Elischer 
273878ed226SJulian Elischer 			if (strncasecmp(command, c->command, len) == 0)
274878ed226SJulian Elischer 				return (c);
275878ed226SJulian Elischer 		} else if (strcasecmp(command, c->command) == 0)
276878ed226SJulian Elischer 				return (c);
277878ed226SJulian Elischer 	}
278878ed226SJulian Elischer 
279878ed226SJulian Elischer 	return (NULL);
280878ed226SJulian Elischer } /* find_hci_command */
281878ed226SJulian Elischer 
282fc5806edSMarkus Brueffer /* Find all HCI nodes */
283fc5806edSMarkus Brueffer static int
284fc5806edSMarkus Brueffer find_hci_nodes(struct nodeinfo** nodes)
285fc5806edSMarkus Brueffer {
286fc5806edSMarkus Brueffer 	struct ng_btsocket_hci_raw_node_list_names	r;
287fc5806edSMarkus Brueffer 	struct sockaddr_hci				addr;
288fc5806edSMarkus Brueffer 	int						s;
289fc5806edSMarkus Brueffer 	const char *					node = "ubt0hci";
290fc5806edSMarkus Brueffer 
291fc5806edSMarkus Brueffer 	r.num_names = MAX_NODE_NUM;
292fc5806edSMarkus Brueffer 	r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo));
293fc5806edSMarkus Brueffer 	if (r.names == NULL)
294fc5806edSMarkus Brueffer 		err(8, "Could not allocate memory");
295fc5806edSMarkus Brueffer 
296fc5806edSMarkus Brueffer 	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
297fc5806edSMarkus Brueffer 	if (s < 0)
298fc5806edSMarkus Brueffer 		err(9, "Could not create socket");
299fc5806edSMarkus Brueffer 
300fc5806edSMarkus Brueffer 	memset(&addr, 0, sizeof(addr));
301fc5806edSMarkus Brueffer 	addr.hci_len = sizeof(addr);
302fc5806edSMarkus Brueffer 	addr.hci_family = AF_BLUETOOTH;
303fc5806edSMarkus Brueffer 	strncpy(addr.hci_node, node, sizeof(addr.hci_node));
304fc5806edSMarkus Brueffer 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
305fc5806edSMarkus Brueffer 		err(10, "Could not bind socket");
306fc5806edSMarkus Brueffer 
307fc5806edSMarkus Brueffer 	if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0)
308fc5806edSMarkus Brueffer 		err(11, "Could not get list of HCI nodes");
309fc5806edSMarkus Brueffer 
310fc5806edSMarkus Brueffer 	close(s);
311fc5806edSMarkus Brueffer 
312fc5806edSMarkus Brueffer 	*nodes = r.names;
313fc5806edSMarkus Brueffer 
314fc5806edSMarkus Brueffer 	return (r.num_names);
315fc5806edSMarkus Brueffer } /* find_hci_nodes */
316fc5806edSMarkus Brueffer 
3170986ab12SMaksim Yevmenkin /* Print commands in specified category */
318878ed226SJulian Elischer static void
319878ed226SJulian Elischer print_hci_command(struct hci_command *category)
320878ed226SJulian Elischer {
321878ed226SJulian Elischer 	struct hci_command	*c = NULL;
322878ed226SJulian Elischer 
323878ed226SJulian Elischer 	for (c = category; c->command != NULL; c++)
324878ed226SJulian Elischer 		fprintf(stdout, "\t%s\n", c->command);
325878ed226SJulian Elischer } /* print_hci_command */
326878ed226SJulian Elischer 
327878ed226SJulian Elischer /* Usage */
328878ed226SJulian Elischer static void
329878ed226SJulian Elischer usage(void)
330878ed226SJulian Elischer {
331fc5806edSMarkus Brueffer 	fprintf(stdout, "Usage: hccontrol [-hN] [-n HCI_node_name] cmd [p1] [..]\n");
332878ed226SJulian Elischer 	exit(255);
333878ed226SJulian Elischer } /* usage */
334878ed226SJulian Elischer 
335