1330449Seadler/*- 2107120Sjulian * hccontrol.c 3107120Sjulian * 4330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5330449Seadler * 6107120Sjulian * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7107120Sjulian * All rights reserved. 8107120Sjulian * 9107120Sjulian * Redistribution and use in source and binary forms, with or without 10107120Sjulian * modification, are permitted provided that the following conditions 11107120Sjulian * are met: 12107120Sjulian * 1. Redistributions of source code must retain the above copyright 13107120Sjulian * notice, this list of conditions and the following disclaimer. 14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright 15107120Sjulian * notice, this list of conditions and the following disclaimer in the 16107120Sjulian * documentation and/or other materials provided with the distribution. 17107120Sjulian * 18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28107120Sjulian * SUCH DAMAGE. 29107120Sjulian * 30121054Semax * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $ 31107120Sjulian * $FreeBSD: stable/11/usr.sbin/bluetooth/hccontrol/hccontrol.c 330449 2018-03-05 07:26:05Z eadler $ 32107120Sjulian */ 33107120Sjulian 34281210Stakawata#define L2CAP_SOCKET_CHECKED 35121054Semax#include <bluetooth.h> 36158834Smarkus#include <sys/ioctl.h> 37107120Sjulian#include <sys/sysctl.h> 38107120Sjulian#include <assert.h> 39107120Sjulian#include <err.h> 40107120Sjulian#include <errno.h> 41158834Smarkus#include <netgraph/ng_message.h> 42107120Sjulian#include <stdio.h> 43107120Sjulian#include <stdlib.h> 44107120Sjulian#include <string.h> 45107120Sjulian#include <unistd.h> 46107120Sjulian#include "hccontrol.h" 47107120Sjulian 48107120Sjulian/* Prototypes */ 49107120Sjulianstatic int do_hci_command (char const *, int, char **); 50107120Sjulianstatic struct hci_command * find_hci_command (char const *, struct hci_command *); 51158834Smarkusstatic int find_hci_nodes (struct nodeinfo **); 52107120Sjulianstatic void print_hci_command (struct hci_command *); 53107120Sjulianstatic void usage (void); 54107120Sjulian 55107120Sjulian/* Globals */ 56107120Sjulianint verbose = 0; 57107120Sjulianint timeout; 58121054Semaxint numeric_bdaddr = 0; 59107120Sjulian 60107120Sjulian/* Main */ 61107120Sjulianint 62107120Sjulianmain(int argc, char *argv[]) 63107120Sjulian{ 64107120Sjulian char *node = NULL; 65107120Sjulian int n; 66107120Sjulian 67107120Sjulian /* Process command line arguments */ 68121054Semax while ((n = getopt(argc, argv, "n:Nvh")) != -1) { 69107120Sjulian switch (n) { 70107120Sjulian case 'n': 71107120Sjulian node = optarg; 72107120Sjulian break; 73107120Sjulian 74121054Semax case 'N': 75121054Semax numeric_bdaddr = 1; 76121054Semax break; 77121054Semax 78107120Sjulian case 'v': 79107120Sjulian verbose = 1; 80107120Sjulian break; 81107120Sjulian 82114879Sjulian case 'h': 83107120Sjulian default: 84107120Sjulian usage(); 85107120Sjulian } 86107120Sjulian } 87107120Sjulian 88107120Sjulian argc -= optind; 89107120Sjulian argv += optind; 90107120Sjulian 91107120Sjulian if (*argv == NULL) 92107120Sjulian usage(); 93107120Sjulian 94107120Sjulian n = do_hci_command(node, argc, argv); 95107120Sjulian 96107120Sjulian return (n); 97107120Sjulian} /* main */ 98107120Sjulian 99107120Sjulian/* Create socket and bind it */ 100107120Sjulianstatic int 101107120Sjuliansocket_open(char const *node) 102107120Sjulian{ 103158834Smarkus struct sockaddr_hci addr; 104158834Smarkus struct ng_btsocket_hci_raw_filter filter; 105159156Smarkus int s, mib[4], num; 106158834Smarkus size_t size; 107158834Smarkus struct nodeinfo *nodes; 108107120Sjulian 109159156Smarkus num = find_hci_nodes(&nodes); 110159156Smarkus if (num == 0) 111159156Smarkus errx(7, "Could not find HCI nodes"); 112107120Sjulian 113158834Smarkus if (node == NULL) { 114158834Smarkus node = strdup(nodes[0].name); 115159156Smarkus if (num > 1) 116159156Smarkus fprintf(stdout, "Using HCI node: %s\n", node); 117158834Smarkus } 118158834Smarkus 119158834Smarkus free(nodes); 120158834Smarkus 121107120Sjulian s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); 122107120Sjulian if (s < 0) 123107120Sjulian err(1, "Could not create socket"); 124107120Sjulian 125107120Sjulian memset(&addr, 0, sizeof(addr)); 126107120Sjulian addr.hci_len = sizeof(addr); 127107120Sjulian addr.hci_family = AF_BLUETOOTH; 128107120Sjulian strncpy(addr.hci_node, node, sizeof(addr.hci_node)); 129107120Sjulian if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) 130107120Sjulian err(2, "Could not bind socket, node=%s", node); 131107120Sjulian 132107120Sjulian if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) 133107120Sjulian err(3, "Could not connect socket, node=%s", node); 134107120Sjulian 135107120Sjulian memset(&filter, 0, sizeof(filter)); 136107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1); 137107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1); 138107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1); 139107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1); 140107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1); 141107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1); 142107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1); 143107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1); 144107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1); 145107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1); 146107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1); 147107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1); 148107120Sjulian bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1); 149281468Stakawata bit_set(filter.event_mask, NG_HCI_EVENT_LE -1); 150107120Sjulian 151107120Sjulian if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, 152107120Sjulian (void * const) &filter, sizeof(filter)) < 0) 153107120Sjulian err(4, "Could not setsockopt()"); 154107120Sjulian 155107120Sjulian size = (sizeof(mib)/sizeof(mib[0])); 156107120Sjulian if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0) 157107120Sjulian err(5, "Could not sysctlnametomib()"); 158107120Sjulian 159107120Sjulian if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), 160107120Sjulian (void *) &timeout, &size, NULL, 0) < 0) 161107120Sjulian err(6, "Could not sysctl()"); 162107120Sjulian 163107120Sjulian timeout ++; 164107120Sjulian 165107120Sjulian return (s); 166107120Sjulian} /* socket_open */ 167107120Sjulian 168107120Sjulian/* Execute commands */ 169107120Sjulianstatic int 170107120Sjuliando_hci_command(char const *node, int argc, char **argv) 171107120Sjulian{ 172107120Sjulian char *cmd = argv[0]; 173107120Sjulian struct hci_command *c = NULL; 174107120Sjulian int s, e, help; 175107120Sjulian 176107120Sjulian help = 0; 177107120Sjulian if (strcasecmp(cmd, "help") == 0) { 178107120Sjulian argc --; 179107120Sjulian argv ++; 180107120Sjulian 181107120Sjulian if (argc <= 0) { 182107120Sjulian fprintf(stdout, "Supported commands:\n"); 183107120Sjulian print_hci_command(link_control_commands); 184107120Sjulian print_hci_command(link_policy_commands); 185107120Sjulian print_hci_command(host_controller_baseband_commands); 186107120Sjulian print_hci_command(info_commands); 187107120Sjulian print_hci_command(status_commands); 188281680Stakawata print_hci_command(le_commands); 189107120Sjulian print_hci_command(node_commands); 190107120Sjulian fprintf(stdout, "\nFor more information use " \ 191107120Sjulian "'help command'\n"); 192107120Sjulian 193107120Sjulian return (OK); 194107120Sjulian } 195107120Sjulian 196107120Sjulian help = 1; 197107120Sjulian cmd = argv[0]; 198107120Sjulian } 199107120Sjulian 200107120Sjulian c = find_hci_command(cmd, link_control_commands); 201107120Sjulian if (c != NULL) 202107120Sjulian goto execute; 203107120Sjulian 204107120Sjulian c = find_hci_command(cmd, link_policy_commands); 205107120Sjulian if (c != NULL) 206107120Sjulian goto execute; 207107120Sjulian 208107120Sjulian c = find_hci_command(cmd, host_controller_baseband_commands); 209107120Sjulian if (c != NULL) 210107120Sjulian goto execute; 211107120Sjulian 212107120Sjulian c = find_hci_command(cmd, info_commands); 213107120Sjulian if (c != NULL) 214107120Sjulian goto execute; 215107120Sjulian 216107120Sjulian c = find_hci_command(cmd, status_commands); 217107120Sjulian if (c != NULL) 218107120Sjulian goto execute; 219107120Sjulian 220281680Stakawata c = find_hci_command(cmd, le_commands); 221281680Stakawata if (c != NULL) 222281680Stakawata goto execute; 223281680Stakawata 224281680Stakawata 225107120Sjulian c = find_hci_command(cmd, node_commands); 226107120Sjulian if (c == NULL) { 227107120Sjulian fprintf(stdout, "Unknown command: \"%s\"\n", cmd); 228107120Sjulian return (ERROR); 229107120Sjulian } 230107120Sjulianexecute: 231107120Sjulian if (!help) { 232107120Sjulian s = socket_open(node); 233107120Sjulian e = (c->handler)(s, -- argc, ++ argv); 234107120Sjulian close(s); 235107120Sjulian } else 236107120Sjulian e = USAGE; 237107120Sjulian 238107120Sjulian switch (e) { 239107120Sjulian case OK: 240107120Sjulian case FAILED: 241107120Sjulian break; 242107120Sjulian 243107120Sjulian case ERROR: 244107120Sjulian fprintf(stdout, "Could not execute command \"%s\". %s\n", 245107120Sjulian cmd, strerror(errno)); 246107120Sjulian break; 247107120Sjulian 248107120Sjulian case USAGE: 249107120Sjulian fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); 250107120Sjulian break; 251107120Sjulian 252107120Sjulian default: assert(0); break; 253107120Sjulian } 254107120Sjulian 255107120Sjulian 256107120Sjulian return (e); 257107120Sjulian} /* do_hci_command */ 258107120Sjulian 259107120Sjulian/* Try to find command in specified category */ 260107120Sjulianstatic struct hci_command * 261107120Sjulianfind_hci_command(char const *command, struct hci_command *category) 262107120Sjulian{ 263107120Sjulian struct hci_command *c = NULL; 264107120Sjulian 265107120Sjulian for (c = category; c->command != NULL; c++) { 266107120Sjulian char *c_end = strchr(c->command, ' '); 267107120Sjulian 268107120Sjulian if (c_end != NULL) { 269107120Sjulian int len = c_end - c->command; 270107120Sjulian 271107120Sjulian if (strncasecmp(command, c->command, len) == 0) 272107120Sjulian return (c); 273107120Sjulian } else if (strcasecmp(command, c->command) == 0) 274107120Sjulian return (c); 275107120Sjulian } 276107120Sjulian 277107120Sjulian return (NULL); 278107120Sjulian} /* find_hci_command */ 279107120Sjulian 280158834Smarkus/* Find all HCI nodes */ 281158834Smarkusstatic int 282158834Smarkusfind_hci_nodes(struct nodeinfo** nodes) 283158834Smarkus{ 284158834Smarkus struct ng_btsocket_hci_raw_node_list_names r; 285158834Smarkus struct sockaddr_hci addr; 286158834Smarkus int s; 287158834Smarkus const char * node = "ubt0hci"; 288158834Smarkus 289158834Smarkus r.num_names = MAX_NODE_NUM; 290158834Smarkus r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo)); 291158834Smarkus if (r.names == NULL) 292158834Smarkus err(8, "Could not allocate memory"); 293158834Smarkus 294158834Smarkus s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); 295158834Smarkus if (s < 0) 296158834Smarkus err(9, "Could not create socket"); 297158834Smarkus 298158834Smarkus memset(&addr, 0, sizeof(addr)); 299158834Smarkus addr.hci_len = sizeof(addr); 300158834Smarkus addr.hci_family = AF_BLUETOOTH; 301158834Smarkus strncpy(addr.hci_node, node, sizeof(addr.hci_node)); 302158834Smarkus if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) 303158834Smarkus err(10, "Could not bind socket"); 304158834Smarkus 305158834Smarkus if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0) 306158834Smarkus err(11, "Could not get list of HCI nodes"); 307158834Smarkus 308158834Smarkus close(s); 309158834Smarkus 310158834Smarkus *nodes = r.names; 311158834Smarkus 312158834Smarkus return (r.num_names); 313158834Smarkus} /* find_hci_nodes */ 314158834Smarkus 315121054Semax/* Print commands in specified category */ 316107120Sjulianstatic void 317107120Sjulianprint_hci_command(struct hci_command *category) 318107120Sjulian{ 319107120Sjulian struct hci_command *c = NULL; 320107120Sjulian 321107120Sjulian for (c = category; c->command != NULL; c++) 322107120Sjulian fprintf(stdout, "\t%s\n", c->command); 323107120Sjulian} /* print_hci_command */ 324107120Sjulian 325107120Sjulian/* Usage */ 326107120Sjulianstatic void 327107120Sjulianusage(void) 328107120Sjulian{ 329158834Smarkus fprintf(stdout, "Usage: hccontrol [-hN] [-n HCI_node_name] cmd [p1] [..]\n"); 330107120Sjulian exit(255); 331107120Sjulian} /* usage */ 332107120Sjulian 333