1 /*-
2  * hccontrol.c
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
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  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $
31  */
32 
33 #define L2CAP_SOCKET_CHECKED
34 #include <bluetooth.h>
35 #include <sys/ioctl.h>
36 #include <sys/sysctl.h>
37 #include <assert.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <netgraph/ng_message.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "hccontrol.h"
46 
47 /* Prototypes */
48 static int                  do_hci_command    (char const *, int, char **);
49 static struct hci_command * find_hci_command  (char const *, struct hci_command *);
50 static int                  find_hci_nodes    (struct nodeinfo **);
51 static void                 print_hci_command (struct hci_command *);
52 static void usage                             (void);
53 
54 /* Globals */
55 int	 verbose = 0;
56 int	 timeout;
57 int	 numeric_bdaddr = 0;
58 
59 /* Main */
60 int
61 main(int argc, char *argv[])
62 {
63 	char	*node = NULL;
64 	int	 n;
65 
66 	/* Process command line arguments */
67 	while ((n = getopt(argc, argv, "n:Nvh")) != -1) {
68 		switch (n) {
69 		case 'n':
70 			node = optarg;
71 			break;
72 
73 		case 'N':
74 			numeric_bdaddr = 1;
75 			break;
76 
77 		case 'v':
78 			verbose = 1;
79 			break;
80 
81 		case 'h':
82 		default:
83 			usage();
84 		}
85 	}
86 
87 	argc -= optind;
88 	argv += optind;
89 
90 	if (*argv == NULL)
91 		usage();
92 
93 	n = do_hci_command(node, argc, argv);
94 
95 	return (n);
96 } /* main */
97 
98 /* Create socket and bind it */
99 static int
100 socket_open(char const *node)
101 {
102 	struct sockaddr_hci			 addr;
103 	struct ng_btsocket_hci_raw_filter	 filter;
104 	int					 s, mib[4], num;
105 	size_t					 size;
106 	struct nodeinfo 			*nodes;
107 	char                                    *lnode = NULL;
108 
109 	num = find_hci_nodes(&nodes);
110 	if (num == 0)
111 		errx(7, "Could not find HCI nodes");
112 
113 	if (node == NULL) {
114 		node = lnode = strdup(nodes[0].name);
115 		if (num > 1)
116 			fprintf(stdout, "Using HCI node: %s\n", node);
117 	}
118 
119 	free(nodes);
120 
121 	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
122 	if (s < 0)
123 		err(1, "Could not create socket");
124 
125 	memset(&addr, 0, sizeof(addr));
126 	addr.hci_len = sizeof(addr);
127 	addr.hci_family = AF_BLUETOOTH;
128 	strncpy(addr.hci_node, node, sizeof(addr.hci_node));
129 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
130 		err(2, "Could not bind socket, node=%s", node);
131 
132 	if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
133 		err(3, "Could not connect socket, node=%s", node);
134 
135 	free(lnode);
136 	memset(&filter, 0, sizeof(filter));
137 	bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1);
138 	bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1);
139 	bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1);
140 	bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1);
141 	bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1);
142 	bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1);
143 	bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1);
144 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1);
145 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1);
146 	bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1);
147 	bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1);
148 	bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1);
149 	bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1);
150 	bit_set(filter.event_mask, NG_HCI_EVENT_LE -1);
151 
152 	if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
153 			(void * const) &filter, sizeof(filter)) < 0)
154 		err(4, "Could not setsockopt()");
155 
156 	size = (sizeof(mib)/sizeof(mib[0]));
157 	if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0)
158 		err(5, "Could not sysctlnametomib()");
159 
160 	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
161 			(void *) &timeout, &size, NULL, 0) < 0)
162 		err(6, "Could not sysctl()");
163 
164 	timeout ++;
165 
166 	return (s);
167 } /* socket_open */
168 
169 /* Execute commands */
170 static int
171 do_hci_command(char const *node, int argc, char **argv)
172 {
173 	char			*cmd = argv[0];
174 	struct hci_command	*c = NULL;
175 	int			 s, e, help;
176 
177 	help = 0;
178 	if (strcasecmp(cmd, "help") == 0) {
179 		argc --;
180 		argv ++;
181 
182 		if (argc <= 0) {
183 			fprintf(stdout, "Supported commands:\n");
184 			print_hci_command(link_control_commands);
185 			print_hci_command(link_policy_commands);
186 			print_hci_command(host_controller_baseband_commands);
187 			print_hci_command(info_commands);
188 			print_hci_command(status_commands);
189 			print_hci_command(le_commands);
190 			print_hci_command(node_commands);
191 			fprintf(stdout, "\nFor more information use " \
192 				"'help command'\n");
193 
194 			return (OK);
195 		}
196 
197 		help = 1;
198 		cmd = argv[0];
199 	}
200 
201 	c = find_hci_command(cmd, link_control_commands);
202 	if (c != NULL)
203 		goto execute;
204 
205 	c = find_hci_command(cmd, link_policy_commands);
206 	if (c != NULL)
207 		goto execute;
208 
209 	c = find_hci_command(cmd, host_controller_baseband_commands);
210 	if (c != NULL)
211 		goto execute;
212 
213 	c = find_hci_command(cmd, info_commands);
214 	if (c != NULL)
215 		goto execute;
216 
217 	c = find_hci_command(cmd, status_commands);
218 	if (c != NULL)
219 		goto execute;
220 
221 	c = find_hci_command(cmd, le_commands);
222 	if (c != NULL)
223 		goto execute;
224 
225 
226 	c = find_hci_command(cmd, node_commands);
227 	if (c == NULL) {
228 		fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
229 		return (ERROR);
230 	}
231 execute:
232 	if (!help) {
233 		s = socket_open(node);
234 		e = (c->handler)(s, -- argc, ++ argv);
235 		close(s);
236 	} else
237 		e = USAGE;
238 
239 	switch (e) {
240 	case OK:
241 	case FAILED:
242 		break;
243 
244 	case ERROR:
245 		fprintf(stdout, "Could not execute command \"%s\". %s\n",
246 			cmd, strerror(errno));
247 		break;
248 
249 	case USAGE:
250 		fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
251 		break;
252 
253 	default: assert(0); break;
254 	}
255 
256 
257 	return (e);
258 } /* do_hci_command */
259 
260 /* Try to find command in specified category */
261 static struct hci_command *
262 find_hci_command(char const *command, struct hci_command *category)
263 {
264 	struct hci_command	*c = NULL;
265 
266 	for (c = category; c->command != NULL; c++) {
267 		char 	*c_end = strchr(c->command, ' ');
268 
269 		if (c_end != NULL) {
270 			int	len = c_end - c->command;
271 
272 			if (strncasecmp(command, c->command, len) == 0)
273 				return (c);
274 		} else if (strcasecmp(command, c->command) == 0)
275 				return (c);
276 	}
277 
278 	return (NULL);
279 } /* find_hci_command */
280 
281 /* Find all HCI nodes */
282 static int
283 find_hci_nodes(struct nodeinfo** nodes)
284 {
285 	struct ng_btsocket_hci_raw_node_list_names	r;
286 	struct sockaddr_hci				addr;
287 	int						s;
288 	const char *					node = "ubt0hci";
289 
290 	r.num_names = MAX_NODE_NUM;
291 	r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo));
292 	if (r.names == NULL)
293 		err(8, "Could not allocate memory");
294 
295 	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
296 	if (s < 0)
297 		err(9, "Could not create socket");
298 
299 	memset(&addr, 0, sizeof(addr));
300 	addr.hci_len = sizeof(addr);
301 	addr.hci_family = AF_BLUETOOTH;
302 	strncpy(addr.hci_node, node, sizeof(addr.hci_node));
303 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
304 		err(10, "Could not bind socket");
305 
306 	if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0)
307 		err(11, "Could not get list of HCI nodes");
308 
309 	close(s);
310 
311 	*nodes = r.names;
312 
313 	return (r.num_names);
314 } /* find_hci_nodes */
315 
316 /* Print commands in specified category */
317 static void
318 print_hci_command(struct hci_command *category)
319 {
320 	struct hci_command	*c = NULL;
321 
322 	for (c = category; c->command != NULL; c++)
323 		fprintf(stdout, "\t%s\n", c->command);
324 } /* print_hci_command */
325 
326 /* Usage */
327 static void
328 usage(void)
329 {
330 	fprintf(stdout, "Usage: hccontrol [-hN] [-n HCI_node_name] cmd [p1] [..]\n");
331 	exit(255);
332 } /* usage */
333 
334