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