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