1bcff2d91STakanori Watanabe /* 2bcff2d91STakanori Watanabe * le.c 3bcff2d91STakanori Watanabe * 4bcff2d91STakanori Watanabe * Copyright (c) 2015 Takanori Watanabe <takawata@freebsd.org> 5bcff2d91STakanori Watanabe * All rights reserved. 6bcff2d91STakanori Watanabe * 7bcff2d91STakanori Watanabe * Redistribution and use in source and binary forms, with or without 8bcff2d91STakanori Watanabe * modification, are permitted provided that the following conditions 9bcff2d91STakanori Watanabe * are met: 10bcff2d91STakanori Watanabe * 1. Redistributions of source code must retain the above copyright 11bcff2d91STakanori Watanabe * notice, this list of conditions and the following disclaimer. 12bcff2d91STakanori Watanabe * 2. Redistributions in binary form must reproduce the above copyright 13bcff2d91STakanori Watanabe * notice, this list of conditions and the following disclaimer in the 14bcff2d91STakanori Watanabe * documentation and/or other materials provided with the distribution. 15bcff2d91STakanori Watanabe * 16bcff2d91STakanori Watanabe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17bcff2d91STakanori Watanabe * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18bcff2d91STakanori Watanabe * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19bcff2d91STakanori Watanabe * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20bcff2d91STakanori Watanabe * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21bcff2d91STakanori Watanabe * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22bcff2d91STakanori Watanabe * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23bcff2d91STakanori Watanabe * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24bcff2d91STakanori Watanabe * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25bcff2d91STakanori Watanabe * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26bcff2d91STakanori Watanabe * SUCH DAMAGE. 27bcff2d91STakanori Watanabe * 28bcff2d91STakanori Watanabe * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $ 29bcff2d91STakanori Watanabe * $FreeBSD$ 30bcff2d91STakanori Watanabe */ 31bcff2d91STakanori Watanabe 32bcff2d91STakanori Watanabe #include <sys/types.h> 33bcff2d91STakanori Watanabe #include <sys/ioctl.h> 34bcff2d91STakanori Watanabe #include <sys/sysctl.h> 35bcff2d91STakanori Watanabe #include <sys/select.h> 36bcff2d91STakanori Watanabe #include <assert.h> 378907f744SAlan Somers #include <bitstring.h> 38bcff2d91STakanori Watanabe #include <err.h> 39bcff2d91STakanori Watanabe #include <errno.h> 40bcff2d91STakanori Watanabe #include <netgraph/ng_message.h> 41bcff2d91STakanori Watanabe #include <errno.h> 429287f06dSTakanori Watanabe #include <stdbool.h> 43bcff2d91STakanori Watanabe #include <stdio.h> 44bcff2d91STakanori Watanabe #include <stdlib.h> 45bcff2d91STakanori Watanabe #include <string.h> 46bcff2d91STakanori Watanabe #include <unistd.h> 474cae2db2STakanori Watanabe #include <stdint.h> 48bcff2d91STakanori Watanabe #define L2CAP_SOCKET_CHECKED 49bcff2d91STakanori Watanabe #include <bluetooth.h> 50bcff2d91STakanori Watanabe #include "hccontrol.h" 519c3471faSMarcelo Araujo 52bcff2d91STakanori Watanabe static int le_set_scan_param(int s, int argc, char *argv[]); 53bcff2d91STakanori Watanabe static int le_set_scan_enable(int s, int argc, char *argv[]); 54bcff2d91STakanori Watanabe static int parse_param(int argc, char *argv[], char *buf, int *len); 55bcff2d91STakanori Watanabe static int le_set_scan_response(int s, int argc, char *argv[]); 5621eefd31SHans Petter Selasky static int le_read_supported_states(int s, int argc, char *argv[]); 57bcff2d91STakanori Watanabe static int le_read_local_supported_features(int s, int argc ,char *argv[]); 58bcff2d91STakanori Watanabe static int set_le_event_mask(int s, uint64_t mask); 59bcff2d91STakanori Watanabe static int set_event_mask(int s, uint64_t mask); 60bcff2d91STakanori Watanabe static int le_enable(int s, int argc, char *argv[]); 61c3f60abcSHans Petter Selasky static int le_set_advertising_enable(int s, int argc, char *argv[]); 62c3f60abcSHans Petter Selasky static int le_set_advertising_param(int s, int argc, char *argv[]); 63c3f60abcSHans Petter Selasky static int le_read_advertising_channel_tx_power(int s, int argc, char *argv[]); 649287f06dSTakanori Watanabe static int le_scan(int s, int argc, char *argv[]); 659287f06dSTakanori Watanabe static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose); 6611fb4bdbSTakanori Watanabe static int le_read_white_list_size(int s, int argc, char *argv[]); 6711fb4bdbSTakanori Watanabe static int le_clear_white_list(int s, int argc, char *argv[]); 6811fb4bdbSTakanori Watanabe static int le_add_device_to_white_list(int s, int argc, char *argv[]); 6911fb4bdbSTakanori Watanabe static int le_remove_device_from_white_list(int s, int argc, char *argv[]); 703ac41cceSTakanori Watanabe static int le_connect(int s, int argc, char *argv[]); 713ac41cceSTakanori Watanabe static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose); 72*2b2c6d69STakanori Watanabe static int le_read_channel_map(int s, int argc, char *argv[]); 73*2b2c6d69STakanori Watanabe static void handle_le_remote_features_event(ng_hci_event_pkt_t* e); 74bcff2d91STakanori Watanabe 759c3471faSMarcelo Araujo static int 769c3471faSMarcelo Araujo le_set_scan_param(int s, int argc, char *argv[]) 77bcff2d91STakanori Watanabe { 78bcff2d91STakanori Watanabe int type; 79bcff2d91STakanori Watanabe int interval; 80bcff2d91STakanori Watanabe int window; 81bcff2d91STakanori Watanabe int adrtype; 82bcff2d91STakanori Watanabe int policy; 83eb2aebeaSTakanori Watanabe int n; 84bcff2d91STakanori Watanabe 85bcff2d91STakanori Watanabe ng_hci_le_set_scan_parameters_cp cp; 86bcff2d91STakanori Watanabe ng_hci_le_set_scan_parameters_rp rp; 87bcff2d91STakanori Watanabe 889c3471faSMarcelo Araujo if (argc != 5) 89eb2aebeaSTakanori Watanabe return (USAGE); 90bcff2d91STakanori Watanabe 919c3471faSMarcelo Araujo if (strcmp(argv[0], "active") == 0) 92bcff2d91STakanori Watanabe type = 1; 939c3471faSMarcelo Araujo else if (strcmp(argv[0], "passive") == 0) 94bcff2d91STakanori Watanabe type = 0; 959c3471faSMarcelo Araujo else 96eb2aebeaSTakanori Watanabe return (USAGE); 97bcff2d91STakanori Watanabe 98bcff2d91STakanori Watanabe interval = (int)(atof(argv[1])/0.625); 99bcff2d91STakanori Watanabe interval = (interval < 4)? 4: interval; 100bcff2d91STakanori Watanabe window = (int)(atof(argv[2])/0.625); 101bcff2d91STakanori Watanabe window = (window < 4) ? 4 : interval; 102bcff2d91STakanori Watanabe 1039c3471faSMarcelo Araujo if (strcmp(argv[3], "public") == 0) 104bcff2d91STakanori Watanabe adrtype = 0; 10505e526fbSTakanori Watanabe else if (strcmp(argv[3], "random") == 0) 106bcff2d91STakanori Watanabe adrtype = 1; 1079c3471faSMarcelo Araujo else 108eb2aebeaSTakanori Watanabe return (USAGE); 109bcff2d91STakanori Watanabe 1109c3471faSMarcelo Araujo if (strcmp(argv[4], "all") == 0) 111bcff2d91STakanori Watanabe policy = 0; 1129c3471faSMarcelo Araujo else if (strcmp(argv[4], "whitelist") == 0) 113bcff2d91STakanori Watanabe policy = 1; 1149c3471faSMarcelo Araujo else 115eb2aebeaSTakanori Watanabe return (USAGE); 116bcff2d91STakanori Watanabe 117bcff2d91STakanori Watanabe cp.le_scan_type = type; 118bcff2d91STakanori Watanabe cp.le_scan_interval = interval; 119bcff2d91STakanori Watanabe cp.own_address_type = adrtype; 120bcff2d91STakanori Watanabe cp.le_scan_window = window; 121bcff2d91STakanori Watanabe cp.scanning_filter_policy = policy; 122bcff2d91STakanori Watanabe n = sizeof(rp); 123bcff2d91STakanori Watanabe 124eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 125eb2aebeaSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_PARAMETERS), 126eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 127eb2aebeaSTakanori Watanabe return (ERROR); 128eb2aebeaSTakanori Watanabe 129eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 130eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 131eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 132eb2aebeaSTakanori Watanabe return (FAILED); 133eb2aebeaSTakanori Watanabe } 134eb2aebeaSTakanori Watanabe 135eb2aebeaSTakanori Watanabe return (OK); 136bcff2d91STakanori Watanabe } 137bcff2d91STakanori Watanabe 1389c3471faSMarcelo Araujo static int 1399c3471faSMarcelo Araujo le_set_scan_enable(int s, int argc, char *argv[]) 140bcff2d91STakanori Watanabe { 141bcff2d91STakanori Watanabe ng_hci_le_set_scan_enable_cp cp; 142bcff2d91STakanori Watanabe ng_hci_le_set_scan_enable_rp rp; 143eb2aebeaSTakanori Watanabe int n, enable = 0; 144bcff2d91STakanori Watanabe 145bcff2d91STakanori Watanabe if (argc != 1) 146eb2aebeaSTakanori Watanabe return (USAGE); 147bcff2d91STakanori Watanabe 1489c3471faSMarcelo Araujo if (strcmp(argv[0], "enable") == 0) 149bcff2d91STakanori Watanabe enable = 1; 1509c3471faSMarcelo Araujo else if (strcmp(argv[0], "disable") != 0) 151eb2aebeaSTakanori Watanabe return (USAGE); 1529c3471faSMarcelo Araujo 153bcff2d91STakanori Watanabe n = sizeof(rp); 154bcff2d91STakanori Watanabe cp.le_scan_enable = enable; 155bcff2d91STakanori Watanabe cp.filter_duplicates = 0; 156eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 157bcff2d91STakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 158eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), 159eb2aebeaSTakanori Watanabe (void *)&rp, &n) == ERROR) 160eb2aebeaSTakanori Watanabe return (ERROR); 161bcff2d91STakanori Watanabe 162eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 163eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 164eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 165eb2aebeaSTakanori Watanabe return (FAILED); 166eb2aebeaSTakanori Watanabe } 167bcff2d91STakanori Watanabe 168eb2aebeaSTakanori Watanabe fprintf(stdout, "LE Scan: %s\n", 169eb2aebeaSTakanori Watanabe enable? "Enabled" : "Disabled"); 170eb2aebeaSTakanori Watanabe 171eb2aebeaSTakanori Watanabe return (OK); 172bcff2d91STakanori Watanabe } 1739c3471faSMarcelo Araujo 1749c3471faSMarcelo Araujo static int 1759c3471faSMarcelo Araujo parse_param(int argc, char *argv[], char *buf, int *len) 176bcff2d91STakanori Watanabe { 177bcff2d91STakanori Watanabe char *buflast = buf + (*len); 178bcff2d91STakanori Watanabe char *curbuf = buf; 179bcff2d91STakanori Watanabe char *token,*lenpos; 180bcff2d91STakanori Watanabe int ch; 181bcff2d91STakanori Watanabe int datalen; 182bcff2d91STakanori Watanabe uint16_t value; 183bcff2d91STakanori Watanabe optreset = 1; 184bcff2d91STakanori Watanabe optind = 0; 185bcff2d91STakanori Watanabe while ((ch = getopt(argc, argv , "n:f:u:")) != -1) { 186bcff2d91STakanori Watanabe switch(ch){ 187bcff2d91STakanori Watanabe case 'n': 188bcff2d91STakanori Watanabe datalen = strlen(optarg); 1899c3471faSMarcelo Araujo if ((curbuf + datalen + 2) >= buflast) 190bcff2d91STakanori Watanabe goto done; 191bcff2d91STakanori Watanabe curbuf[0] = datalen + 1; 192bcff2d91STakanori Watanabe curbuf[1] = 8; 193bcff2d91STakanori Watanabe curbuf += 2; 194bcff2d91STakanori Watanabe memcpy(curbuf, optarg, datalen); 195bcff2d91STakanori Watanabe curbuf += datalen; 196bcff2d91STakanori Watanabe break; 197bcff2d91STakanori Watanabe case 'f': 1989c3471faSMarcelo Araujo if (curbuf+3 > buflast) 199bcff2d91STakanori Watanabe goto done; 200bcff2d91STakanori Watanabe curbuf[0] = 2; 201bcff2d91STakanori Watanabe curbuf[1] = 1; 20232f32669SHans Petter Selasky curbuf[2] = (uint8_t)strtol(optarg, NULL, 16); 203bcff2d91STakanori Watanabe curbuf += 3; 204bcff2d91STakanori Watanabe break; 205bcff2d91STakanori Watanabe case 'u': 206bcff2d91STakanori Watanabe if ((buf+2) >= buflast) 207bcff2d91STakanori Watanabe goto done; 20832f32669SHans Petter Selasky lenpos = curbuf; 209bcff2d91STakanori Watanabe curbuf[1] = 2; 210bcff2d91STakanori Watanabe *lenpos = 1; 211bcff2d91STakanori Watanabe curbuf += 2; 212bcff2d91STakanori Watanabe while ((token = strsep(&optarg, ",")) != NULL) { 213bcff2d91STakanori Watanabe value = strtol(token, NULL, 16); 214bcff2d91STakanori Watanabe if ((curbuf+2) >= buflast) 215bcff2d91STakanori Watanabe break; 216bcff2d91STakanori Watanabe curbuf[0] = value &0xff; 217bcff2d91STakanori Watanabe curbuf[1] = (value>>8)&0xff; 218bcff2d91STakanori Watanabe curbuf += 2; 21932f32669SHans Petter Selasky *lenpos += 2; 220bcff2d91STakanori Watanabe } 221bcff2d91STakanori Watanabe 222bcff2d91STakanori Watanabe } 223bcff2d91STakanori Watanabe } 224bcff2d91STakanori Watanabe done: 225bcff2d91STakanori Watanabe *len = curbuf - buf; 226bcff2d91STakanori Watanabe 227eb2aebeaSTakanori Watanabe return (OK); 228bcff2d91STakanori Watanabe } 229bcff2d91STakanori Watanabe 2309c3471faSMarcelo Araujo static int 2319c3471faSMarcelo Araujo le_set_scan_response(int s, int argc, char *argv[]) 232bcff2d91STakanori Watanabe { 233bcff2d91STakanori Watanabe ng_hci_le_set_scan_response_data_cp cp; 234bcff2d91STakanori Watanabe ng_hci_le_set_scan_response_data_rp rp; 235bcff2d91STakanori Watanabe int n; 236bcff2d91STakanori Watanabe int len; 237bcff2d91STakanori Watanabe char buf[NG_HCI_ADVERTISING_DATA_SIZE]; 2389c3471faSMarcelo Araujo 239bcff2d91STakanori Watanabe len = sizeof(buf); 240bcff2d91STakanori Watanabe parse_param(argc, argv, buf, &len); 241bcff2d91STakanori Watanabe memset(cp.scan_response_data, 0, sizeof(cp.scan_response_data)); 242bcff2d91STakanori Watanabe cp.scan_response_data_length = len; 243bcff2d91STakanori Watanabe memcpy(cp.scan_response_data, buf, len); 244bcff2d91STakanori Watanabe n = sizeof(rp); 245eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 246bcff2d91STakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA), 247eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), 248eb2aebeaSTakanori Watanabe (void *)&rp, &n) == ERROR) 249eb2aebeaSTakanori Watanabe return (ERROR); 250bcff2d91STakanori Watanabe 251eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 252eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 253eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 254eb2aebeaSTakanori Watanabe return (FAILED); 255eb2aebeaSTakanori Watanabe } 256bcff2d91STakanori Watanabe 257eb2aebeaSTakanori Watanabe return (OK); 258bcff2d91STakanori Watanabe } 259bcff2d91STakanori Watanabe 2609c3471faSMarcelo Araujo static int 2619c3471faSMarcelo Araujo le_read_local_supported_features(int s, int argc ,char *argv[]) 262bcff2d91STakanori Watanabe { 263bcff2d91STakanori Watanabe ng_hci_le_read_local_supported_features_rp rp; 264bcff2d91STakanori Watanabe int n = sizeof(rp); 2659c3471faSMarcelo Araujo 266ea011491SHans Petter Selasky union { 267ea011491SHans Petter Selasky uint64_t raw; 268ea011491SHans Petter Selasky uint8_t octets[8]; 269ea011491SHans Petter Selasky } le_features; 270ea011491SHans Petter Selasky 271ea011491SHans Petter Selasky char buffer[2048]; 272ea011491SHans Petter Selasky 273ea011491SHans Petter Selasky if (hci_simple_request(s, 274bcff2d91STakanori Watanabe NG_HCI_OPCODE(NG_HCI_OGF_LE, 275bcff2d91STakanori Watanabe NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES), 276ea011491SHans Petter Selasky (void *)&rp, &n) == ERROR) 277ea011491SHans Petter Selasky return (ERROR); 2789c3471faSMarcelo Araujo 279ea011491SHans Petter Selasky if (rp.status != 0x00) { 280ea011491SHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 281ea011491SHans Petter Selasky hci_status2str(rp.status), rp.status); 282ea011491SHans Petter Selasky return (FAILED); 283ea011491SHans Petter Selasky } 284bcff2d91STakanori Watanabe 285ea011491SHans Petter Selasky le_features.raw = rp.le_features; 286ea011491SHans Petter Selasky 287ea011491SHans Petter Selasky fprintf(stdout, "LE Features: "); 288ea011491SHans Petter Selasky for(int i = 0; i < 8; i++) 289ea011491SHans Petter Selasky fprintf(stdout, " %#02x", le_features.octets[i]); 290ea011491SHans Petter Selasky fprintf(stdout, "\n%s\n", hci_le_features2str(le_features.octets, 291ea011491SHans Petter Selasky buffer, sizeof(buffer))); 292ea011491SHans Petter Selasky fprintf(stdout, "\n"); 293ea011491SHans Petter Selasky 294eb2aebeaSTakanori Watanabe return (OK); 295bcff2d91STakanori Watanabe } 2969c3471faSMarcelo Araujo 2979c3471faSMarcelo Araujo static int 29821eefd31SHans Petter Selasky le_read_supported_states(int s, int argc, char *argv[]) 299bcff2d91STakanori Watanabe { 30021eefd31SHans Petter Selasky ng_hci_le_read_supported_states_rp rp; 301bcff2d91STakanori Watanabe int n = sizeof(rp); 3029c3471faSMarcelo Araujo 30321eefd31SHans Petter Selasky if (hci_simple_request(s, NG_HCI_OPCODE( 3049c3471faSMarcelo Araujo NG_HCI_OGF_LE, 30521eefd31SHans Petter Selasky NG_HCI_OCF_LE_READ_SUPPORTED_STATES), 30621eefd31SHans Petter Selasky (void *)&rp, &n) == ERROR) 30721eefd31SHans Petter Selasky return (ERROR); 3089c3471faSMarcelo Araujo 30921eefd31SHans Petter Selasky if (rp.status != 0x00) { 31021eefd31SHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 31121eefd31SHans Petter Selasky hci_status2str(rp.status), rp.status); 31221eefd31SHans Petter Selasky return (FAILED); 31321eefd31SHans Petter Selasky } 314bcff2d91STakanori Watanabe 31521eefd31SHans Petter Selasky fprintf(stdout, "LE States: %jx\n", rp.le_states); 31621eefd31SHans Petter Selasky 31721eefd31SHans Petter Selasky return (OK); 318bcff2d91STakanori Watanabe } 319bcff2d91STakanori Watanabe 3209c3471faSMarcelo Araujo static int 3219c3471faSMarcelo Araujo set_le_event_mask(int s, uint64_t mask) 322bcff2d91STakanori Watanabe { 323bcff2d91STakanori Watanabe ng_hci_le_set_event_mask_cp semc; 324bcff2d91STakanori Watanabe ng_hci_le_set_event_mask_rp rp; 325eb2aebeaSTakanori Watanabe int i, n; 326bcff2d91STakanori Watanabe 327bcff2d91STakanori Watanabe n = sizeof(rp); 328bcff2d91STakanori Watanabe 329bcff2d91STakanori Watanabe for (i=0; i < NG_HCI_LE_EVENT_MASK_SIZE; i++) { 330bcff2d91STakanori Watanabe semc.event_mask[i] = mask&0xff; 331bcff2d91STakanori Watanabe mask >>= 8; 332bcff2d91STakanori Watanabe } 333eb2aebeaSTakanori Watanabe if(hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 3349c3471faSMarcelo Araujo NG_HCI_OCF_LE_SET_EVENT_MASK), 335eb2aebeaSTakanori Watanabe (void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR) 336eb2aebeaSTakanori Watanabe return (ERROR); 337bcff2d91STakanori Watanabe 338eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 339eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 340eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 341eb2aebeaSTakanori Watanabe return (FAILED); 342eb2aebeaSTakanori Watanabe } 343eb2aebeaSTakanori Watanabe 344eb2aebeaSTakanori Watanabe return (OK); 345bcff2d91STakanori Watanabe } 346bcff2d91STakanori Watanabe 3479c3471faSMarcelo Araujo static int 3489c3471faSMarcelo Araujo set_event_mask(int s, uint64_t mask) 349bcff2d91STakanori Watanabe { 350bcff2d91STakanori Watanabe ng_hci_set_event_mask_cp semc; 351bcff2d91STakanori Watanabe ng_hci_set_event_mask_rp rp; 352eb2aebeaSTakanori Watanabe int i, n; 353bcff2d91STakanori Watanabe 354bcff2d91STakanori Watanabe n = sizeof(rp); 355bcff2d91STakanori Watanabe 356bcff2d91STakanori Watanabe for (i=0; i < NG_HCI_EVENT_MASK_SIZE; i++) { 357bcff2d91STakanori Watanabe semc.event_mask[i] = mask&0xff; 358bcff2d91STakanori Watanabe mask >>= 8; 359bcff2d91STakanori Watanabe } 360eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, 3619c3471faSMarcelo Araujo NG_HCI_OCF_SET_EVENT_MASK), 362eb2aebeaSTakanori Watanabe (void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR) 363eb2aebeaSTakanori Watanabe return (ERROR); 364bcff2d91STakanori Watanabe 365eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 366eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 367eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 368eb2aebeaSTakanori Watanabe return (FAILED); 369eb2aebeaSTakanori Watanabe } 370eb2aebeaSTakanori Watanabe 371eb2aebeaSTakanori Watanabe return (OK); 372bcff2d91STakanori Watanabe } 373bcff2d91STakanori Watanabe 3749c3471faSMarcelo Araujo static 3759c3471faSMarcelo Araujo int le_enable(int s, int argc, char *argv[]) 376bcff2d91STakanori Watanabe { 377eb2aebeaSTakanori Watanabe int result; 378eb2aebeaSTakanori Watanabe 3799c3471faSMarcelo Araujo if (argc != 1) 380eb2aebeaSTakanori Watanabe return (USAGE); 381bcff2d91STakanori Watanabe 382bcff2d91STakanori Watanabe if (strcasecmp(argv[0], "enable") == 0) { 383eb2aebeaSTakanori Watanabe result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT | 384bcff2d91STakanori Watanabe NG_HCI_EVENT_MASK_LE); 385eb2aebeaSTakanori Watanabe if (result != OK) 386eb2aebeaSTakanori Watanabe return result; 387eb2aebeaSTakanori Watanabe result = set_le_event_mask(s, NG_HCI_LE_EVENT_MASK_ALL); 388eb2aebeaSTakanori Watanabe if (result == OK) { 389eb2aebeaSTakanori Watanabe fprintf(stdout, "LE enabled\n"); 390eb2aebeaSTakanori Watanabe return (OK); 391eb2aebeaSTakanori Watanabe } else 392eb2aebeaSTakanori Watanabe return result; 393eb2aebeaSTakanori Watanabe } else if (strcasecmp(argv[0], "disable") == 0) { 394eb2aebeaSTakanori Watanabe result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT); 395eb2aebeaSTakanori Watanabe if (result == OK) { 396eb2aebeaSTakanori Watanabe fprintf(stdout, "LE disabled\n"); 397eb2aebeaSTakanori Watanabe return (OK); 398eb2aebeaSTakanori Watanabe } else 399eb2aebeaSTakanori Watanabe return result; 400eb2aebeaSTakanori Watanabe } else 401eb2aebeaSTakanori Watanabe return (USAGE); 402bcff2d91STakanori Watanabe } 403bcff2d91STakanori Watanabe 404c3f60abcSHans Petter Selasky static int 405c3f60abcSHans Petter Selasky le_set_advertising_enable(int s, int argc, char *argv[]) 406c3f60abcSHans Petter Selasky { 407c3f60abcSHans Petter Selasky ng_hci_le_set_advertise_enable_cp cp; 408c3f60abcSHans Petter Selasky ng_hci_le_set_advertise_enable_rp rp; 409c3f60abcSHans Petter Selasky int n, enable = 0; 410c3f60abcSHans Petter Selasky 411c3f60abcSHans Petter Selasky if (argc != 1) 412c3f60abcSHans Petter Selasky return USAGE; 413c3f60abcSHans Petter Selasky 414c3f60abcSHans Petter Selasky if (strcmp(argv[0], "enable") == 0) 415c3f60abcSHans Petter Selasky enable = 1; 416c3f60abcSHans Petter Selasky else if (strcmp(argv[0], "disable") != 0) 417c3f60abcSHans Petter Selasky return USAGE; 418c3f60abcSHans Petter Selasky 419c3f60abcSHans Petter Selasky n = sizeof(rp); 420c3f60abcSHans Petter Selasky cp.advertising_enable = enable; 421c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 422c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE), 423c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 424c3f60abcSHans Petter Selasky return (ERROR); 425c3f60abcSHans Petter Selasky 426c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 427c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 428c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 429c3f60abcSHans Petter Selasky return (FAILED); 430c3f60abcSHans Petter Selasky } 431c3f60abcSHans Petter Selasky fprintf(stdout, "LE Advertising %s\n", (enable ? "enabled" : "disabled")); 432c3f60abcSHans Petter Selasky 433c3f60abcSHans Petter Selasky return (OK); 434c3f60abcSHans Petter Selasky } 435c3f60abcSHans Petter Selasky 436c3f60abcSHans Petter Selasky static int 437c3f60abcSHans Petter Selasky le_set_advertising_param(int s, int argc, char *argv[]) 438c3f60abcSHans Petter Selasky { 439c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_parameters_cp cp; 440c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_parameters_rp rp; 441c3f60abcSHans Petter Selasky 442c3f60abcSHans Petter Selasky int n, ch; 443c3f60abcSHans Petter Selasky 444c3f60abcSHans Petter Selasky cp.advertising_interval_min = 0x800; 445c3f60abcSHans Petter Selasky cp.advertising_interval_max = 0x800; 446c3f60abcSHans Petter Selasky cp.advertising_type = 0; 447c3f60abcSHans Petter Selasky cp.own_address_type = 0; 448c3f60abcSHans Petter Selasky cp.direct_address_type = 0; 449c3f60abcSHans Petter Selasky 450c3f60abcSHans Petter Selasky cp.advertising_channel_map = 7; 451c3f60abcSHans Petter Selasky cp.advertising_filter_policy = 0; 452c3f60abcSHans Petter Selasky 453c3f60abcSHans Petter Selasky optreset = 1; 454c3f60abcSHans Petter Selasky optind = 0; 455c3f60abcSHans Petter Selasky while ((ch = getopt(argc, argv , "m:M:t:o:p:a:c:f:")) != -1) { 456c3f60abcSHans Petter Selasky switch(ch) { 457c3f60abcSHans Petter Selasky case 'm': 458c3f60abcSHans Petter Selasky cp.advertising_interval_min = 459c3f60abcSHans Petter Selasky (uint16_t)(strtod(optarg, NULL)/0.625); 460c3f60abcSHans Petter Selasky break; 461c3f60abcSHans Petter Selasky case 'M': 462c3f60abcSHans Petter Selasky cp.advertising_interval_max = 463c3f60abcSHans Petter Selasky (uint16_t)(strtod(optarg, NULL)/0.625); 464c3f60abcSHans Petter Selasky break; 465c3f60abcSHans Petter Selasky case 't': 466c3f60abcSHans Petter Selasky cp.advertising_type = 467c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 468c3f60abcSHans Petter Selasky break; 469c3f60abcSHans Petter Selasky case 'o': 470c3f60abcSHans Petter Selasky cp.own_address_type = 471c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 472c3f60abcSHans Petter Selasky break; 473c3f60abcSHans Petter Selasky case 'p': 474c3f60abcSHans Petter Selasky cp.direct_address_type = 475c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 476c3f60abcSHans Petter Selasky break; 477c3f60abcSHans Petter Selasky case 'a': 478c3f60abcSHans Petter Selasky if (!bt_aton(optarg, &cp.direct_address)) { 479c3f60abcSHans Petter Selasky struct hostent *he = NULL; 480c3f60abcSHans Petter Selasky 481c3f60abcSHans Petter Selasky if ((he = bt_gethostbyname(optarg)) == NULL) 482c3f60abcSHans Petter Selasky return (USAGE); 483c3f60abcSHans Petter Selasky 484c3f60abcSHans Petter Selasky memcpy(&cp.direct_address, he->h_addr, sizeof(cp.direct_address)); 485c3f60abcSHans Petter Selasky } 486c3f60abcSHans Petter Selasky break; 487c3f60abcSHans Petter Selasky case 'c': 488c3f60abcSHans Petter Selasky cp.advertising_channel_map = 489c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 490c3f60abcSHans Petter Selasky break; 491c3f60abcSHans Petter Selasky case 'f': 492c3f60abcSHans Petter Selasky cp.advertising_filter_policy = 493c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 494c3f60abcSHans Petter Selasky break; 495c3f60abcSHans Petter Selasky } 496c3f60abcSHans Petter Selasky } 497c3f60abcSHans Petter Selasky 498c3f60abcSHans Petter Selasky n = sizeof(rp); 499c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 500c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS), 501c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 502c3f60abcSHans Petter Selasky return (ERROR); 503c3f60abcSHans Petter Selasky 504c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 505c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 506c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 507c3f60abcSHans Petter Selasky return (FAILED); 508c3f60abcSHans Petter Selasky } 509c3f60abcSHans Petter Selasky 510c3f60abcSHans Petter Selasky return (OK); 511c3f60abcSHans Petter Selasky } 512c3f60abcSHans Petter Selasky 513c3f60abcSHans Petter Selasky static int 514c3f60abcSHans Petter Selasky le_read_advertising_channel_tx_power(int s, int argc, char *argv[]) 515c3f60abcSHans Petter Selasky { 516c3f60abcSHans Petter Selasky ng_hci_le_read_advertising_channel_tx_power_rp rp; 517c3f60abcSHans Petter Selasky int n; 518c3f60abcSHans Petter Selasky 519c3f60abcSHans Petter Selasky n = sizeof(rp); 520c3f60abcSHans Petter Selasky 521c3f60abcSHans Petter Selasky if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 522c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER), 523c3f60abcSHans Petter Selasky (void *)&rp, &n) == ERROR) 524c3f60abcSHans Petter Selasky return (ERROR); 525c3f60abcSHans Petter Selasky 526c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 527c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 528c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 529c3f60abcSHans Petter Selasky return (FAILED); 530c3f60abcSHans Petter Selasky } 531c3f60abcSHans Petter Selasky 532c3f60abcSHans Petter Selasky fprintf(stdout, "Advertising transmit power level: %d dBm\n", 533c3f60abcSHans Petter Selasky (int8_t)rp.transmit_power_level); 534c3f60abcSHans Petter Selasky 535c3f60abcSHans Petter Selasky return (OK); 536c3f60abcSHans Petter Selasky } 537c3f60abcSHans Petter Selasky 538c3f60abcSHans Petter Selasky static int 539c3f60abcSHans Petter Selasky le_set_advertising_data(int s, int argc, char *argv[]) 540c3f60abcSHans Petter Selasky { 541c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_data_cp cp; 542c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_data_rp rp; 543c3f60abcSHans Petter Selasky int n, len; 544c3f60abcSHans Petter Selasky 545c3f60abcSHans Petter Selasky n = sizeof(rp); 546c3f60abcSHans Petter Selasky 547c3f60abcSHans Petter Selasky char buf[NG_HCI_ADVERTISING_DATA_SIZE]; 548c3f60abcSHans Petter Selasky 549c3f60abcSHans Petter Selasky len = sizeof(buf); 550c3f60abcSHans Petter Selasky parse_param(argc, argv, buf, &len); 551c3f60abcSHans Petter Selasky memset(cp.advertising_data, 0, sizeof(cp.advertising_data)); 552c3f60abcSHans Petter Selasky cp.advertising_data_length = len; 5537b2f84dbSHans Petter Selasky memcpy(cp.advertising_data, buf, len); 554c3f60abcSHans Petter Selasky 555c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 556c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISING_DATA), 557c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 558c3f60abcSHans Petter Selasky return (ERROR); 559c3f60abcSHans Petter Selasky 560c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 561c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 562c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 563c3f60abcSHans Petter Selasky return (FAILED); 564c3f60abcSHans Petter Selasky } 565c3f60abcSHans Petter Selasky 566c3f60abcSHans Petter Selasky return (OK); 567c3f60abcSHans Petter Selasky } 5681f5d883dSTakanori Watanabe static int 5691f5d883dSTakanori Watanabe le_read_buffer_size(int s, int argc, char *argv[]) 5701f5d883dSTakanori Watanabe { 5711f5d883dSTakanori Watanabe union { 5721f5d883dSTakanori Watanabe ng_hci_le_read_buffer_size_rp v1; 5731f5d883dSTakanori Watanabe ng_hci_le_read_buffer_size_rp_v2 v2; 5741f5d883dSTakanori Watanabe } rp; 5751f5d883dSTakanori Watanabe 5761f5d883dSTakanori Watanabe int n, ch; 5771f5d883dSTakanori Watanabe uint8_t v; 5781f5d883dSTakanori Watanabe uint16_t cmd; 5791f5d883dSTakanori Watanabe 5801f5d883dSTakanori Watanabe optreset = 1; 5811f5d883dSTakanori Watanabe optind = 0; 5821f5d883dSTakanori Watanabe 5831f5d883dSTakanori Watanabe /* Default to version 1*/ 5841f5d883dSTakanori Watanabe v = 1; 5851f5d883dSTakanori Watanabe cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE; 5861f5d883dSTakanori Watanabe 5871f5d883dSTakanori Watanabe while ((ch = getopt(argc, argv , "v:")) != -1) { 5881f5d883dSTakanori Watanabe switch(ch) { 5891f5d883dSTakanori Watanabe case 'v': 5901f5d883dSTakanori Watanabe v = (uint8_t)strtol(optarg, NULL, 16); 5911f5d883dSTakanori Watanabe if (v == 2) 5921f5d883dSTakanori Watanabe cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE_V2; 5931f5d883dSTakanori Watanabe else if (v > 2) 5941f5d883dSTakanori Watanabe return (USAGE); 5951f5d883dSTakanori Watanabe break; 5961f5d883dSTakanori Watanabe default: 5971f5d883dSTakanori Watanabe v = 1; 5981f5d883dSTakanori Watanabe } 5991f5d883dSTakanori Watanabe } 6001f5d883dSTakanori Watanabe 6011f5d883dSTakanori Watanabe n = sizeof(rp); 6021f5d883dSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, cmd), 6031f5d883dSTakanori Watanabe (void *)&rp, &n) == ERROR) 6041f5d883dSTakanori Watanabe return (ERROR); 6051f5d883dSTakanori Watanabe 6061f5d883dSTakanori Watanabe if (rp.v1.status != 0x00) { 6071f5d883dSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 6081f5d883dSTakanori Watanabe hci_status2str(rp.v1.status), rp.v1.status); 6091f5d883dSTakanori Watanabe return (FAILED); 6101f5d883dSTakanori Watanabe } 6111f5d883dSTakanori Watanabe 6121f5d883dSTakanori Watanabe fprintf(stdout, "ACL data packet length: %d\n", 6131f5d883dSTakanori Watanabe rp.v1.hc_le_data_packet_length); 6141f5d883dSTakanori Watanabe fprintf(stdout, "Number of ACL data packets: %d\n", 6151f5d883dSTakanori Watanabe rp.v1.hc_total_num_le_data_packets); 6161f5d883dSTakanori Watanabe 6171f5d883dSTakanori Watanabe if (v == 2) { 6181f5d883dSTakanori Watanabe fprintf(stdout, "ISO data packet length: %d\n", 6191f5d883dSTakanori Watanabe rp.v2.hc_iso_data_packet_length); 6201f5d883dSTakanori Watanabe fprintf(stdout, "Number of ISO data packets: %d\n", 6211f5d883dSTakanori Watanabe rp.v2.hc_total_num_iso_data_packets); 6221f5d883dSTakanori Watanabe } 6231f5d883dSTakanori Watanabe 6241f5d883dSTakanori Watanabe return (OK); 6251f5d883dSTakanori Watanabe } 626c3f60abcSHans Petter Selasky 6279287f06dSTakanori Watanabe static int 6289287f06dSTakanori Watanabe le_scan(int s, int argc, char *argv[]) 6299287f06dSTakanori Watanabe { 6309287f06dSTakanori Watanabe int n, bufsize, scancount, numscans; 6319287f06dSTakanori Watanabe bool verbose; 6329287f06dSTakanori Watanabe uint8_t active = 0; 6339287f06dSTakanori Watanabe char ch; 6349287f06dSTakanori Watanabe 6359287f06dSTakanori Watanabe char b[512]; 6369287f06dSTakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 6379287f06dSTakanori Watanabe 6389287f06dSTakanori Watanabe ng_hci_le_set_scan_parameters_cp scan_param_cp; 6399287f06dSTakanori Watanabe ng_hci_le_set_scan_parameters_rp scan_param_rp; 6409287f06dSTakanori Watanabe 6419287f06dSTakanori Watanabe ng_hci_le_set_scan_enable_cp scan_enable_cp; 6429287f06dSTakanori Watanabe ng_hci_le_set_scan_enable_rp scan_enable_rp; 6439287f06dSTakanori Watanabe 6449287f06dSTakanori Watanabe optreset = 1; 6459287f06dSTakanori Watanabe optind = 0; 6469287f06dSTakanori Watanabe verbose = false; 6479287f06dSTakanori Watanabe numscans = 1; 6489287f06dSTakanori Watanabe 6499287f06dSTakanori Watanabe while ((ch = getopt(argc, argv , "an:v")) != -1) { 6509287f06dSTakanori Watanabe switch(ch) { 6519287f06dSTakanori Watanabe case 'a': 6529287f06dSTakanori Watanabe active = 1; 6539287f06dSTakanori Watanabe break; 6549287f06dSTakanori Watanabe case 'n': 6559287f06dSTakanori Watanabe numscans = (uint8_t)strtol(optarg, NULL, 10); 6569287f06dSTakanori Watanabe break; 6579287f06dSTakanori Watanabe case 'v': 6589287f06dSTakanori Watanabe verbose = true; 6599287f06dSTakanori Watanabe break; 6609287f06dSTakanori Watanabe } 6619287f06dSTakanori Watanabe } 6629287f06dSTakanori Watanabe 6639287f06dSTakanori Watanabe scan_param_cp.le_scan_type = active; 6649287f06dSTakanori Watanabe scan_param_cp.le_scan_interval = (uint16_t)(100/0.625); 6659287f06dSTakanori Watanabe scan_param_cp.le_scan_window = (uint16_t)(50/0.625); 6669287f06dSTakanori Watanabe /* Address type public */ 6679287f06dSTakanori Watanabe scan_param_cp.own_address_type = 0; 6689287f06dSTakanori Watanabe /* 'All' filter policy */ 6699287f06dSTakanori Watanabe scan_param_cp.scanning_filter_policy = 0; 6709287f06dSTakanori Watanabe n = sizeof(scan_param_rp); 6719287f06dSTakanori Watanabe 6729287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 6739287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_PARAMETERS), 6749287f06dSTakanori Watanabe (void *)&scan_param_cp, sizeof(scan_param_cp), 6759287f06dSTakanori Watanabe (void *)&scan_param_rp, &n) == ERROR) 6769287f06dSTakanori Watanabe return (ERROR); 6779287f06dSTakanori Watanabe 6789287f06dSTakanori Watanabe if (scan_param_rp.status != 0x00) { 6799287f06dSTakanori Watanabe fprintf(stdout, "LE_Set_Scan_Parameters failed. Status: %s [%#02x]\n", 6809287f06dSTakanori Watanabe hci_status2str(scan_param_rp.status), 6819287f06dSTakanori Watanabe scan_param_rp.status); 6829287f06dSTakanori Watanabe return (FAILED); 6839287f06dSTakanori Watanabe } 6849287f06dSTakanori Watanabe 6859287f06dSTakanori Watanabe /* Enable scanning */ 6869287f06dSTakanori Watanabe n = sizeof(scan_enable_rp); 6879287f06dSTakanori Watanabe scan_enable_cp.le_scan_enable = 1; 6889287f06dSTakanori Watanabe scan_enable_cp.filter_duplicates = 1; 6899287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 6909287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 6919287f06dSTakanori Watanabe (void *)&scan_enable_cp, sizeof(scan_enable_cp), 6929287f06dSTakanori Watanabe (void *)&scan_enable_rp, &n) == ERROR) 6939287f06dSTakanori Watanabe return (ERROR); 6949287f06dSTakanori Watanabe 6959287f06dSTakanori Watanabe if (scan_enable_rp.status != 0x00) { 6969287f06dSTakanori Watanabe fprintf(stdout, "LE_Scan_Enable enable failed. Status: %s [%#02x]\n", 6979287f06dSTakanori Watanabe hci_status2str(scan_enable_rp.status), 6989287f06dSTakanori Watanabe scan_enable_rp.status); 6999287f06dSTakanori Watanabe return (FAILED); 7009287f06dSTakanori Watanabe } 7019287f06dSTakanori Watanabe 7029287f06dSTakanori Watanabe scancount = 0; 7039287f06dSTakanori Watanabe while (scancount < numscans) { 7049287f06dSTakanori Watanabe /* wait for scan events */ 7059287f06dSTakanori Watanabe bufsize = sizeof(b); 7069287f06dSTakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 7079287f06dSTakanori Watanabe return (ERROR); 7089287f06dSTakanori Watanabe } 7099287f06dSTakanori Watanabe 7109287f06dSTakanori Watanabe if (bufsize < sizeof(*e)) { 7119287f06dSTakanori Watanabe errno = EIO; 7129287f06dSTakanori Watanabe return (ERROR); 7139287f06dSTakanori Watanabe } 7149287f06dSTakanori Watanabe scancount++; 7159287f06dSTakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 7169287f06dSTakanori Watanabe fprintf(stdout, "Scan %d\n", scancount); 7179287f06dSTakanori Watanabe handle_le_event(e, verbose); 7189287f06dSTakanori Watanabe } 7199287f06dSTakanori Watanabe } 7209287f06dSTakanori Watanabe 7219287f06dSTakanori Watanabe fprintf(stdout, "Scan complete\n"); 7229287f06dSTakanori Watanabe 7239287f06dSTakanori Watanabe /* Disable scanning */ 7249287f06dSTakanori Watanabe n = sizeof(scan_enable_rp); 7259287f06dSTakanori Watanabe scan_enable_cp.le_scan_enable = 0; 7269287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 7279287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 7289287f06dSTakanori Watanabe (void *)&scan_enable_cp, sizeof(scan_enable_cp), 7299287f06dSTakanori Watanabe (void *)&scan_enable_rp, &n) == ERROR) 7309287f06dSTakanori Watanabe return (ERROR); 7319287f06dSTakanori Watanabe 7329287f06dSTakanori Watanabe if (scan_enable_rp.status != 0x00) { 7339287f06dSTakanori Watanabe fprintf(stdout, "LE_Scan_Enable disable failed. Status: %s [%#02x]\n", 7349287f06dSTakanori Watanabe hci_status2str(scan_enable_rp.status), 7359287f06dSTakanori Watanabe scan_enable_rp.status); 7369287f06dSTakanori Watanabe return (FAILED); 7379287f06dSTakanori Watanabe } 7389287f06dSTakanori Watanabe 7399287f06dSTakanori Watanabe return (OK); 7409287f06dSTakanori Watanabe } 7419287f06dSTakanori Watanabe 7429287f06dSTakanori Watanabe static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose) 7439287f06dSTakanori Watanabe { 7449287f06dSTakanori Watanabe int rc; 7459287f06dSTakanori Watanabe ng_hci_le_ep *leer = 7469287f06dSTakanori Watanabe (ng_hci_le_ep *)(e + 1); 7479287f06dSTakanori Watanabe ng_hci_le_advertising_report_ep *advrep = 7489287f06dSTakanori Watanabe (ng_hci_le_advertising_report_ep *)(leer + 1); 7499287f06dSTakanori Watanabe ng_hci_le_advreport *reports = 7509287f06dSTakanori Watanabe (ng_hci_le_advreport *)(advrep + 1); 7519287f06dSTakanori Watanabe 7529287f06dSTakanori Watanabe if (leer->subevent_code == NG_HCI_LEEV_ADVREP) { 7539287f06dSTakanori Watanabe fprintf(stdout, "Scan result, num_reports: %d\n", 7549287f06dSTakanori Watanabe advrep->num_reports); 7559287f06dSTakanori Watanabe for(rc = 0; rc < advrep->num_reports; rc++) { 7569287f06dSTakanori Watanabe uint8_t length = (uint8_t)reports[rc].length_data; 7579287f06dSTakanori Watanabe fprintf(stdout, "\tBD_ADDR %s \n", 7589287f06dSTakanori Watanabe hci_bdaddr2str(&reports[rc].bdaddr)); 7599287f06dSTakanori Watanabe fprintf(stdout, "\tAddress type: %s\n", 7609287f06dSTakanori Watanabe hci_addrtype2str(reports[rc].addr_type)); 7619287f06dSTakanori Watanabe if (length > 0 && verbose) { 7629287f06dSTakanori Watanabe dump_adv_data(length, reports[rc].data); 7639287f06dSTakanori Watanabe print_adv_data(length, reports[rc].data); 7649287f06dSTakanori Watanabe fprintf(stdout, 7659287f06dSTakanori Watanabe "\tRSSI: %d dBm\n", 7669287f06dSTakanori Watanabe (int8_t)reports[rc].data[length]); 7679287f06dSTakanori Watanabe fprintf(stdout, "\n"); 7689287f06dSTakanori Watanabe } 7699287f06dSTakanori Watanabe } 7709287f06dSTakanori Watanabe } 7719287f06dSTakanori Watanabe } 7729287f06dSTakanori Watanabe 77311fb4bdbSTakanori Watanabe static int 77411fb4bdbSTakanori Watanabe le_read_white_list_size(int s, int argc, char *argv[]) 77511fb4bdbSTakanori Watanabe { 77611fb4bdbSTakanori Watanabe ng_hci_le_read_white_list_size_rp rp; 77711fb4bdbSTakanori Watanabe int n; 77811fb4bdbSTakanori Watanabe 77911fb4bdbSTakanori Watanabe n = sizeof(rp); 78011fb4bdbSTakanori Watanabe 78111fb4bdbSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 78211fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE), 78311fb4bdbSTakanori Watanabe (void *)&rp, &n) == ERROR) 78411fb4bdbSTakanori Watanabe return (ERROR); 78511fb4bdbSTakanori Watanabe 78611fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 78711fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 78811fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 78911fb4bdbSTakanori Watanabe return (FAILED); 79011fb4bdbSTakanori Watanabe } 79111fb4bdbSTakanori Watanabe 79211fb4bdbSTakanori Watanabe fprintf(stdout, "White list size: %d\n", 79311fb4bdbSTakanori Watanabe (uint8_t)rp.white_list_size); 79411fb4bdbSTakanori Watanabe 79511fb4bdbSTakanori Watanabe return (OK); 79611fb4bdbSTakanori Watanabe } 79711fb4bdbSTakanori Watanabe 79811fb4bdbSTakanori Watanabe static int 79911fb4bdbSTakanori Watanabe le_clear_white_list(int s, int argc, char *argv[]) 80011fb4bdbSTakanori Watanabe { 80111fb4bdbSTakanori Watanabe ng_hci_le_clear_white_list_rp rp; 80211fb4bdbSTakanori Watanabe int n; 80311fb4bdbSTakanori Watanabe 80411fb4bdbSTakanori Watanabe n = sizeof(rp); 80511fb4bdbSTakanori Watanabe 80611fb4bdbSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 80711fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_CLEAR_WHITE_LIST), 80811fb4bdbSTakanori Watanabe (void *)&rp, &n) == ERROR) 80911fb4bdbSTakanori Watanabe return (ERROR); 81011fb4bdbSTakanori Watanabe 81111fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 81211fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 81311fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 81411fb4bdbSTakanori Watanabe return (FAILED); 81511fb4bdbSTakanori Watanabe } 81611fb4bdbSTakanori Watanabe 81711fb4bdbSTakanori Watanabe fprintf(stdout, "White list cleared\n"); 81811fb4bdbSTakanori Watanabe 81911fb4bdbSTakanori Watanabe return (OK); 82011fb4bdbSTakanori Watanabe } 82111fb4bdbSTakanori Watanabe 82211fb4bdbSTakanori Watanabe static int 82311fb4bdbSTakanori Watanabe le_add_device_to_white_list(int s, int argc, char *argv[]) 82411fb4bdbSTakanori Watanabe { 82511fb4bdbSTakanori Watanabe ng_hci_le_add_device_to_white_list_cp cp; 82611fb4bdbSTakanori Watanabe ng_hci_le_add_device_to_white_list_rp rp; 82711fb4bdbSTakanori Watanabe int n; 82811fb4bdbSTakanori Watanabe char ch; 82911fb4bdbSTakanori Watanabe optreset = 1; 83011fb4bdbSTakanori Watanabe optind = 0; 83111fb4bdbSTakanori Watanabe bool addr_set = false; 83211fb4bdbSTakanori Watanabe 83311fb4bdbSTakanori Watanabe n = sizeof(rp); 83411fb4bdbSTakanori Watanabe 83511fb4bdbSTakanori Watanabe cp.address_type = 0x00; 83611fb4bdbSTakanori Watanabe 83711fb4bdbSTakanori Watanabe while ((ch = getopt(argc, argv , "t:a:")) != -1) { 83811fb4bdbSTakanori Watanabe switch(ch) { 83911fb4bdbSTakanori Watanabe case 't': 84011fb4bdbSTakanori Watanabe if (strcmp(optarg, "public") == 0) 84111fb4bdbSTakanori Watanabe cp.address_type = 0x00; 84211fb4bdbSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 84311fb4bdbSTakanori Watanabe cp.address_type = 0x01; 84411fb4bdbSTakanori Watanabe else 84511fb4bdbSTakanori Watanabe return (USAGE); 84611fb4bdbSTakanori Watanabe break; 84711fb4bdbSTakanori Watanabe case 'a': 84811fb4bdbSTakanori Watanabe addr_set = true; 84911fb4bdbSTakanori Watanabe if (!bt_aton(optarg, &cp.address)) { 85011fb4bdbSTakanori Watanabe struct hostent *he = NULL; 85111fb4bdbSTakanori Watanabe 85211fb4bdbSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 85311fb4bdbSTakanori Watanabe return (USAGE); 85411fb4bdbSTakanori Watanabe 85511fb4bdbSTakanori Watanabe memcpy(&cp.address, he->h_addr, 85611fb4bdbSTakanori Watanabe sizeof(cp.address)); 85711fb4bdbSTakanori Watanabe } 85811fb4bdbSTakanori Watanabe break; 85911fb4bdbSTakanori Watanabe } 86011fb4bdbSTakanori Watanabe } 86111fb4bdbSTakanori Watanabe 86211fb4bdbSTakanori Watanabe if (addr_set == false) 86311fb4bdbSTakanori Watanabe return (USAGE); 86411fb4bdbSTakanori Watanabe 86511fb4bdbSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 86611fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST), 86711fb4bdbSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 86811fb4bdbSTakanori Watanabe return (ERROR); 86911fb4bdbSTakanori Watanabe 87011fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 87111fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 87211fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 87311fb4bdbSTakanori Watanabe return (FAILED); 87411fb4bdbSTakanori Watanabe } 87511fb4bdbSTakanori Watanabe 87611fb4bdbSTakanori Watanabe fprintf(stdout, "Address added to white list\n"); 87711fb4bdbSTakanori Watanabe 87811fb4bdbSTakanori Watanabe return (OK); 87911fb4bdbSTakanori Watanabe } 88011fb4bdbSTakanori Watanabe 88111fb4bdbSTakanori Watanabe static int 88211fb4bdbSTakanori Watanabe le_remove_device_from_white_list(int s, int argc, char *argv[]) 88311fb4bdbSTakanori Watanabe { 88411fb4bdbSTakanori Watanabe ng_hci_le_remove_device_from_white_list_cp cp; 88511fb4bdbSTakanori Watanabe ng_hci_le_remove_device_from_white_list_rp rp; 88611fb4bdbSTakanori Watanabe int n; 88711fb4bdbSTakanori Watanabe char ch; 88811fb4bdbSTakanori Watanabe optreset = 1; 88911fb4bdbSTakanori Watanabe optind = 0; 89011fb4bdbSTakanori Watanabe bool addr_set = false; 89111fb4bdbSTakanori Watanabe 89211fb4bdbSTakanori Watanabe n = sizeof(rp); 89311fb4bdbSTakanori Watanabe 89411fb4bdbSTakanori Watanabe cp.address_type = 0x00; 89511fb4bdbSTakanori Watanabe 89611fb4bdbSTakanori Watanabe while ((ch = getopt(argc, argv , "t:a:")) != -1) { 89711fb4bdbSTakanori Watanabe switch(ch) { 89811fb4bdbSTakanori Watanabe case 't': 89911fb4bdbSTakanori Watanabe if (strcmp(optarg, "public") == 0) 90011fb4bdbSTakanori Watanabe cp.address_type = 0x00; 90111fb4bdbSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 90211fb4bdbSTakanori Watanabe cp.address_type = 0x01; 90311fb4bdbSTakanori Watanabe else 90411fb4bdbSTakanori Watanabe return (USAGE); 90511fb4bdbSTakanori Watanabe break; 90611fb4bdbSTakanori Watanabe case 'a': 90711fb4bdbSTakanori Watanabe addr_set = true; 90811fb4bdbSTakanori Watanabe if (!bt_aton(optarg, &cp.address)) { 90911fb4bdbSTakanori Watanabe struct hostent *he = NULL; 91011fb4bdbSTakanori Watanabe 91111fb4bdbSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 91211fb4bdbSTakanori Watanabe return (USAGE); 91311fb4bdbSTakanori Watanabe 91411fb4bdbSTakanori Watanabe memcpy(&cp.address, he->h_addr, 91511fb4bdbSTakanori Watanabe sizeof(cp.address)); 91611fb4bdbSTakanori Watanabe } 91711fb4bdbSTakanori Watanabe break; 91811fb4bdbSTakanori Watanabe } 91911fb4bdbSTakanori Watanabe } 92011fb4bdbSTakanori Watanabe 92111fb4bdbSTakanori Watanabe if (addr_set == false) 92211fb4bdbSTakanori Watanabe return (USAGE); 92311fb4bdbSTakanori Watanabe 92411fb4bdbSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 92511fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST), 92611fb4bdbSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 92711fb4bdbSTakanori Watanabe return (ERROR); 92811fb4bdbSTakanori Watanabe 92911fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 93011fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 93111fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 93211fb4bdbSTakanori Watanabe return (FAILED); 93311fb4bdbSTakanori Watanabe } 93411fb4bdbSTakanori Watanabe 93511fb4bdbSTakanori Watanabe fprintf(stdout, "Address removed from white list\n"); 93611fb4bdbSTakanori Watanabe 93711fb4bdbSTakanori Watanabe return (OK); 93811fb4bdbSTakanori Watanabe } 93911fb4bdbSTakanori Watanabe 9403ac41cceSTakanori Watanabe static int 9413ac41cceSTakanori Watanabe le_connect(int s, int argc, char *argv[]) 9423ac41cceSTakanori Watanabe { 9433ac41cceSTakanori Watanabe ng_hci_le_create_connection_cp cp; 9443ac41cceSTakanori Watanabe ng_hci_status_rp rp; 9453ac41cceSTakanori Watanabe char b[512]; 9463ac41cceSTakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 9473ac41cceSTakanori Watanabe 9483ac41cceSTakanori Watanabe int n, scancount, bufsize; 9493ac41cceSTakanori Watanabe char ch; 9503ac41cceSTakanori Watanabe bool addr_set = false; 9513ac41cceSTakanori Watanabe bool verbose = false; 9523ac41cceSTakanori Watanabe 9533ac41cceSTakanori Watanabe optreset = 1; 9543ac41cceSTakanori Watanabe optind = 0; 9553ac41cceSTakanori Watanabe 9563ac41cceSTakanori Watanabe /* minimal scan interval (2.5ms) */ 9573ac41cceSTakanori Watanabe cp.scan_interval = htole16(4); 9583ac41cceSTakanori Watanabe cp.scan_window = htole16(4); 9593ac41cceSTakanori Watanabe 9603ac41cceSTakanori Watanabe /* Don't use the whitelist */ 9613ac41cceSTakanori Watanabe cp.filter_policy = 0x00; 9623ac41cceSTakanori Watanabe 9633ac41cceSTakanori Watanabe /* Default to public peer address */ 9643ac41cceSTakanori Watanabe cp.peer_addr_type = 0x00; 9653ac41cceSTakanori Watanabe 9663ac41cceSTakanori Watanabe /* Own address type public */ 9673ac41cceSTakanori Watanabe cp.own_address_type = 0x00; 9683ac41cceSTakanori Watanabe 9693ac41cceSTakanori Watanabe /* 18.75ms min connection interval */ 9703ac41cceSTakanori Watanabe cp.conn_interval_min = htole16(0x000F); 9713ac41cceSTakanori Watanabe /* 18.75ms max connection interval */ 9723ac41cceSTakanori Watanabe cp.conn_interval_max = htole16(0x000F); 9733ac41cceSTakanori Watanabe 9743ac41cceSTakanori Watanabe /* 0 events connection latency */ 9753ac41cceSTakanori Watanabe cp.conn_latency = htole16(0x0000); 9763ac41cceSTakanori Watanabe 9773ac41cceSTakanori Watanabe /* 32s supervision timeout */ 9783ac41cceSTakanori Watanabe cp.supervision_timeout = htole16(0x0C80); 9793ac41cceSTakanori Watanabe 9803ac41cceSTakanori Watanabe /* Min CE Length 0.625 ms */ 9813ac41cceSTakanori Watanabe cp.min_ce_length = htole16(1); 9823ac41cceSTakanori Watanabe /* Max CE Length 0.625 ms */ 9833ac41cceSTakanori Watanabe cp.max_ce_length = htole16(1); 9843ac41cceSTakanori Watanabe 9853ac41cceSTakanori Watanabe while ((ch = getopt(argc, argv , "a:t:v")) != -1) { 9863ac41cceSTakanori Watanabe switch(ch) { 9873ac41cceSTakanori Watanabe case 't': 9883ac41cceSTakanori Watanabe if (strcmp(optarg, "public") == 0) 9893ac41cceSTakanori Watanabe cp.peer_addr_type = 0x00; 9903ac41cceSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 9913ac41cceSTakanori Watanabe cp.peer_addr_type = 0x01; 9923ac41cceSTakanori Watanabe else 9933ac41cceSTakanori Watanabe return (USAGE); 9943ac41cceSTakanori Watanabe break; 9953ac41cceSTakanori Watanabe case 'a': 9963ac41cceSTakanori Watanabe addr_set = true; 9973ac41cceSTakanori Watanabe if (!bt_aton(optarg, &cp.peer_addr)) { 9983ac41cceSTakanori Watanabe struct hostent *he = NULL; 9993ac41cceSTakanori Watanabe 10003ac41cceSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 10013ac41cceSTakanori Watanabe return (USAGE); 10023ac41cceSTakanori Watanabe 10033ac41cceSTakanori Watanabe memcpy(&cp.peer_addr, he->h_addr, 10043ac41cceSTakanori Watanabe sizeof(cp.peer_addr)); 10053ac41cceSTakanori Watanabe } 10063ac41cceSTakanori Watanabe break; 10073ac41cceSTakanori Watanabe case 'v': 10083ac41cceSTakanori Watanabe verbose = true; 10093ac41cceSTakanori Watanabe break; 10103ac41cceSTakanori Watanabe } 10113ac41cceSTakanori Watanabe } 10123ac41cceSTakanori Watanabe 10133ac41cceSTakanori Watanabe if (addr_set == false) 10143ac41cceSTakanori Watanabe return (USAGE); 10153ac41cceSTakanori Watanabe 10163ac41cceSTakanori Watanabe n = sizeof(rp); 10173ac41cceSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 10183ac41cceSTakanori Watanabe NG_HCI_OCF_LE_CREATE_CONNECTION), 10193ac41cceSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 10203ac41cceSTakanori Watanabe return (ERROR); 10213ac41cceSTakanori Watanabe 10223ac41cceSTakanori Watanabe if (rp.status != 0x00) { 10233ac41cceSTakanori Watanabe fprintf(stdout, 10243ac41cceSTakanori Watanabe "Create connection failed. Status: %s [%#02x]\n", 10253ac41cceSTakanori Watanabe hci_status2str(rp.status), rp.status); 10263ac41cceSTakanori Watanabe return (FAILED); 10273ac41cceSTakanori Watanabe } 10283ac41cceSTakanori Watanabe 10293ac41cceSTakanori Watanabe scancount = 0; 10303ac41cceSTakanori Watanabe while (scancount < 3) { 10313ac41cceSTakanori Watanabe /* wait for connection events */ 10323ac41cceSTakanori Watanabe bufsize = sizeof(b); 10333ac41cceSTakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 10343ac41cceSTakanori Watanabe return (ERROR); 10353ac41cceSTakanori Watanabe } 10363ac41cceSTakanori Watanabe 10373ac41cceSTakanori Watanabe if (bufsize < sizeof(*e)) { 10383ac41cceSTakanori Watanabe errno = EIO; 10393ac41cceSTakanori Watanabe return (ERROR); 10403ac41cceSTakanori Watanabe } 10413ac41cceSTakanori Watanabe scancount++; 10423ac41cceSTakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 10433ac41cceSTakanori Watanabe handle_le_connection_event(e, verbose); 10443ac41cceSTakanori Watanabe break; 10453ac41cceSTakanori Watanabe } 10463ac41cceSTakanori Watanabe } 10473ac41cceSTakanori Watanabe 10483ac41cceSTakanori Watanabe return (OK); 10493ac41cceSTakanori Watanabe } 10503ac41cceSTakanori Watanabe 10513ac41cceSTakanori Watanabe static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose) 10523ac41cceSTakanori Watanabe { 10533ac41cceSTakanori Watanabe ng_hci_le_ep *ev_pkt; 10543ac41cceSTakanori Watanabe ng_hci_le_connection_complete_ep *conn_event; 10553ac41cceSTakanori Watanabe 10563ac41cceSTakanori Watanabe ev_pkt = (ng_hci_le_ep *)(e + 1); 10573ac41cceSTakanori Watanabe 10583ac41cceSTakanori Watanabe if (ev_pkt->subevent_code == NG_HCI_LEEV_CON_COMPL) { 10593ac41cceSTakanori Watanabe conn_event =(ng_hci_le_connection_complete_ep *)(ev_pkt + 1); 10603ac41cceSTakanori Watanabe fprintf(stdout, "Handle: %d\n", le16toh(conn_event->handle)); 10613ac41cceSTakanori Watanabe if (verbose) { 10623ac41cceSTakanori Watanabe fprintf(stdout, 10633ac41cceSTakanori Watanabe "Status: %s\n", 10643ac41cceSTakanori Watanabe hci_status2str(conn_event->status)); 10653ac41cceSTakanori Watanabe fprintf(stdout, 10663ac41cceSTakanori Watanabe "Role: %s\n", 10673ac41cceSTakanori Watanabe hci_role2str(conn_event->role)); 10683ac41cceSTakanori Watanabe fprintf(stdout, 10693ac41cceSTakanori Watanabe "Address Type: %s\n", 10703ac41cceSTakanori Watanabe hci_addrtype2str(conn_event->address_type)); 10713ac41cceSTakanori Watanabe fprintf(stdout, 10723ac41cceSTakanori Watanabe "Address: %s\n", 10733ac41cceSTakanori Watanabe hci_bdaddr2str(&conn_event->address)); 10743ac41cceSTakanori Watanabe fprintf(stdout, 10753ac41cceSTakanori Watanabe "Interval: %.2fms\n", 10763ac41cceSTakanori Watanabe 6.25 * le16toh(conn_event->interval)); 10773ac41cceSTakanori Watanabe fprintf(stdout, 10783ac41cceSTakanori Watanabe "Latency: %d events\n", conn_event->latency); 10793ac41cceSTakanori Watanabe fprintf(stdout, 10803ac41cceSTakanori Watanabe "Supervision timeout: %dms\n", 10813ac41cceSTakanori Watanabe 10 * le16toh(conn_event->supervision_timeout)); 10823ac41cceSTakanori Watanabe fprintf(stdout, 1083f8143ed7STakanori Watanabe "Master clock accuracy: %s\n", 10843ac41cceSTakanori Watanabe hci_mc_accuracy2str( 10853ac41cceSTakanori Watanabe conn_event->master_clock_accuracy)); 10863ac41cceSTakanori Watanabe } 10873ac41cceSTakanori Watanabe } 10883ac41cceSTakanori Watanabe return; 10893ac41cceSTakanori Watanabe } 10903ac41cceSTakanori Watanabe 1091*2b2c6d69STakanori Watanabe static int 1092*2b2c6d69STakanori Watanabe le_read_channel_map(int s, int argc, char *argv[]) 1093*2b2c6d69STakanori Watanabe { 1094*2b2c6d69STakanori Watanabe ng_hci_le_read_channel_map_cp cp; 1095*2b2c6d69STakanori Watanabe ng_hci_le_read_channel_map_rp rp; 1096*2b2c6d69STakanori Watanabe int n; 1097*2b2c6d69STakanori Watanabe char buffer[2048]; 1098*2b2c6d69STakanori Watanabe 1099*2b2c6d69STakanori Watanabe /* parse command parameters */ 1100*2b2c6d69STakanori Watanabe switch (argc) { 1101*2b2c6d69STakanori Watanabe case 1: 1102*2b2c6d69STakanori Watanabe /* connection handle */ 1103*2b2c6d69STakanori Watanabe if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) 1104*2b2c6d69STakanori Watanabe return (USAGE); 1105*2b2c6d69STakanori Watanabe 1106*2b2c6d69STakanori Watanabe cp.connection_handle = (uint16_t) (n & 0x0fff); 1107*2b2c6d69STakanori Watanabe cp.connection_handle = htole16(cp.connection_handle); 1108*2b2c6d69STakanori Watanabe break; 1109*2b2c6d69STakanori Watanabe 1110*2b2c6d69STakanori Watanabe default: 1111*2b2c6d69STakanori Watanabe return (USAGE); 1112*2b2c6d69STakanori Watanabe } 1113*2b2c6d69STakanori Watanabe 1114*2b2c6d69STakanori Watanabe n = sizeof(rp); 1115*2b2c6d69STakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 1116*2b2c6d69STakanori Watanabe NG_HCI_OCF_LE_READ_CHANNEL_MAP), 1117*2b2c6d69STakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 1118*2b2c6d69STakanori Watanabe return (ERROR); 1119*2b2c6d69STakanori Watanabe 1120*2b2c6d69STakanori Watanabe if (rp.status != 0x00) { 1121*2b2c6d69STakanori Watanabe fprintf(stdout, 1122*2b2c6d69STakanori Watanabe "Read channel map failed. Status: %s [%#02x]\n", 1123*2b2c6d69STakanori Watanabe hci_status2str(rp.status), rp.status); 1124*2b2c6d69STakanori Watanabe return (FAILED); 1125*2b2c6d69STakanori Watanabe } 1126*2b2c6d69STakanori Watanabe 1127*2b2c6d69STakanori Watanabe fprintf(stdout, "Connection handle: %d\n", 1128*2b2c6d69STakanori Watanabe le16toh(rp.connection_handle)); 1129*2b2c6d69STakanori Watanabe fprintf(stdout, "Used channels:\n"); 1130*2b2c6d69STakanori Watanabe fprintf(stdout, "\n%s\n", hci_le_chanmap2str(rp.le_channel_map, 1131*2b2c6d69STakanori Watanabe buffer, sizeof(buffer))); 1132*2b2c6d69STakanori Watanabe 1133*2b2c6d69STakanori Watanabe return (OK); 1134*2b2c6d69STakanori Watanabe } /* le_read_channel_map */ 1135*2b2c6d69STakanori Watanabe 1136*2b2c6d69STakanori Watanabe static int 1137*2b2c6d69STakanori Watanabe le_read_remote_features(int s, int argc, char *argv[]) 1138*2b2c6d69STakanori Watanabe { 1139*2b2c6d69STakanori Watanabe ng_hci_le_read_remote_used_features_cp cp; 1140*2b2c6d69STakanori Watanabe ng_hci_status_rp rp; 1141*2b2c6d69STakanori Watanabe int n, bufsize; 1142*2b2c6d69STakanori Watanabe char b[512]; 1143*2b2c6d69STakanori Watanabe 1144*2b2c6d69STakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 1145*2b2c6d69STakanori Watanabe 1146*2b2c6d69STakanori Watanabe /* parse command parameters */ 1147*2b2c6d69STakanori Watanabe switch (argc) { 1148*2b2c6d69STakanori Watanabe case 1: 1149*2b2c6d69STakanori Watanabe /* connection handle */ 1150*2b2c6d69STakanori Watanabe if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) 1151*2b2c6d69STakanori Watanabe return (USAGE); 1152*2b2c6d69STakanori Watanabe 1153*2b2c6d69STakanori Watanabe cp.connection_handle = (uint16_t) (n & 0x0fff); 1154*2b2c6d69STakanori Watanabe cp.connection_handle = htole16(cp.connection_handle); 1155*2b2c6d69STakanori Watanabe break; 1156*2b2c6d69STakanori Watanabe 1157*2b2c6d69STakanori Watanabe default: 1158*2b2c6d69STakanori Watanabe return (USAGE); 1159*2b2c6d69STakanori Watanabe } 1160*2b2c6d69STakanori Watanabe 1161*2b2c6d69STakanori Watanabe n = sizeof(rp); 1162*2b2c6d69STakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 1163*2b2c6d69STakanori Watanabe NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES), 1164*2b2c6d69STakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 1165*2b2c6d69STakanori Watanabe return (ERROR); 1166*2b2c6d69STakanori Watanabe 1167*2b2c6d69STakanori Watanabe if (rp.status != 0x00) { 1168*2b2c6d69STakanori Watanabe fprintf(stdout, 1169*2b2c6d69STakanori Watanabe "Read remote features failed. Status: %s [%#02x]\n", 1170*2b2c6d69STakanori Watanabe hci_status2str(rp.status), rp.status); 1171*2b2c6d69STakanori Watanabe return (FAILED); 1172*2b2c6d69STakanori Watanabe } 1173*2b2c6d69STakanori Watanabe 1174*2b2c6d69STakanori Watanabe /* wait for connection events */ 1175*2b2c6d69STakanori Watanabe bufsize = sizeof(b); 1176*2b2c6d69STakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 1177*2b2c6d69STakanori Watanabe return (ERROR); 1178*2b2c6d69STakanori Watanabe } 1179*2b2c6d69STakanori Watanabe 1180*2b2c6d69STakanori Watanabe if (bufsize < sizeof(*e)) { 1181*2b2c6d69STakanori Watanabe errno = EIO; 1182*2b2c6d69STakanori Watanabe return (ERROR); 1183*2b2c6d69STakanori Watanabe } 1184*2b2c6d69STakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 1185*2b2c6d69STakanori Watanabe handle_le_remote_features_event(e); 1186*2b2c6d69STakanori Watanabe } 1187*2b2c6d69STakanori Watanabe 1188*2b2c6d69STakanori Watanabe return (OK); 1189*2b2c6d69STakanori Watanabe } /* le_read_remote_features */ 1190*2b2c6d69STakanori Watanabe 1191*2b2c6d69STakanori Watanabe static void handle_le_remote_features_event(ng_hci_event_pkt_t* e) 1192*2b2c6d69STakanori Watanabe { 1193*2b2c6d69STakanori Watanabe ng_hci_le_ep *ev_pkt; 1194*2b2c6d69STakanori Watanabe ng_hci_le_read_remote_features_ep *feat_event; 1195*2b2c6d69STakanori Watanabe char buffer[2048]; 1196*2b2c6d69STakanori Watanabe 1197*2b2c6d69STakanori Watanabe ev_pkt = (ng_hci_le_ep *)(e + 1); 1198*2b2c6d69STakanori Watanabe 1199*2b2c6d69STakanori Watanabe if (ev_pkt->subevent_code == NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL) { 1200*2b2c6d69STakanori Watanabe feat_event =(ng_hci_le_read_remote_features_ep *)(ev_pkt + 1); 1201*2b2c6d69STakanori Watanabe fprintf(stdout, "Handle: %d\n", 1202*2b2c6d69STakanori Watanabe le16toh(feat_event->connection_handle)); 1203*2b2c6d69STakanori Watanabe fprintf(stdout, 1204*2b2c6d69STakanori Watanabe "Status: %s\n", 1205*2b2c6d69STakanori Watanabe hci_status2str(feat_event->status)); 1206*2b2c6d69STakanori Watanabe fprintf(stdout, "Features:\n%s\n", 1207*2b2c6d69STakanori Watanabe hci_le_features2str(feat_event->features, 1208*2b2c6d69STakanori Watanabe buffer, sizeof(buffer))); 1209*2b2c6d69STakanori Watanabe } 1210*2b2c6d69STakanori Watanabe 1211*2b2c6d69STakanori Watanabe return; 1212*2b2c6d69STakanori Watanabe } /* handle_le_remote_features_event */ 1213*2b2c6d69STakanori Watanabe 1214*2b2c6d69STakanori Watanabe 1215*2b2c6d69STakanori Watanabe 1216bcff2d91STakanori Watanabe struct hci_command le_commands[] = { 1217bcff2d91STakanori Watanabe { 1218bcff2d91STakanori Watanabe "le_enable", 1219bcff2d91STakanori Watanabe "le_enable [enable|disable] \n" 1220bcff2d91STakanori Watanabe "Enable LE event ", 1221bcff2d91STakanori Watanabe &le_enable, 1222bcff2d91STakanori Watanabe }, 1223bcff2d91STakanori Watanabe { 1224bcff2d91STakanori Watanabe "le_read_local_supported_features", 1225bcff2d91STakanori Watanabe "le_read_local_supported_features\n" 1226bcff2d91STakanori Watanabe "read local supported features mask", 1227bcff2d91STakanori Watanabe &le_read_local_supported_features, 1228bcff2d91STakanori Watanabe }, 1229bcff2d91STakanori Watanabe { 123021eefd31SHans Petter Selasky "le_read_supported_states", 123121eefd31SHans Petter Selasky "le_read_supported_states\n" 1232bcff2d91STakanori Watanabe "read supported status" 1233bcff2d91STakanori Watanabe , 123421eefd31SHans Petter Selasky &le_read_supported_states, 1235bcff2d91STakanori Watanabe }, 1236bcff2d91STakanori Watanabe { 1237bcff2d91STakanori Watanabe "le_set_scan_response", 1238bcff2d91STakanori Watanabe "le_set_scan_response -n $name -f $flag -u $uuid16,$uuid16 \n" 1239bcff2d91STakanori Watanabe "set LE scan response data" 1240bcff2d91STakanori Watanabe , 1241bcff2d91STakanori Watanabe &le_set_scan_response, 1242bcff2d91STakanori Watanabe }, 1243bcff2d91STakanori Watanabe { 1244bcff2d91STakanori Watanabe "le_set_scan_enable", 1245bcff2d91STakanori Watanabe "le_set_scan_enable [enable|disable] \n" 1246bcff2d91STakanori Watanabe "enable or disable LE device scan", 1247bcff2d91STakanori Watanabe &le_set_scan_enable 1248bcff2d91STakanori Watanabe }, 1249bcff2d91STakanori Watanabe { 1250bcff2d91STakanori Watanabe "le_set_scan_param", 1251bcff2d91STakanori Watanabe "le_set_scan_param [active|passive] interval(ms) window(ms) [public|random] [all|whitelist] \n" 1252bcff2d91STakanori Watanabe "set LE device scan parameter", 1253bcff2d91STakanori Watanabe &le_set_scan_param 1254bcff2d91STakanori Watanabe }, 1255c3f60abcSHans Petter Selasky { 1256c3f60abcSHans Petter Selasky "le_set_advertising_enable", 1257c3f60abcSHans Petter Selasky "le_set_advertising_enable [enable|disable] \n" 1258c3f60abcSHans Petter Selasky "start or stop advertising", 1259c3f60abcSHans Petter Selasky &le_set_advertising_enable 1260c3f60abcSHans Petter Selasky }, 1261c3f60abcSHans Petter Selasky { 1262c3f60abcSHans Petter Selasky "le_read_advertising_channel_tx_power", 1263c3f60abcSHans Petter Selasky "le_read_advertising_channel_tx_power\n" 1264c3f60abcSHans Petter Selasky "read host advertising transmit poser level (dBm)", 1265c3f60abcSHans Petter Selasky &le_read_advertising_channel_tx_power 1266c3f60abcSHans Petter Selasky }, 1267c3f60abcSHans Petter Selasky { 1268c3f60abcSHans Petter Selasky "le_set_advertising_param", 1269c3f60abcSHans Petter Selasky "le_set_advertising_param [-m min_interval(ms)] [-M max_interval(ms)]\n" 1270c3f60abcSHans Petter Selasky "[-t advertising_type] [-o own_address_type] [-p peer_address_type]\n" 1271c3f60abcSHans Petter Selasky "[-c advertising_channel_map] [-f advertising_filter_policy]\n" 1272c3f60abcSHans Petter Selasky "[-a peer_address]\n" 1273c3f60abcSHans Petter Selasky "set LE device advertising parameters", 1274c3f60abcSHans Petter Selasky &le_set_advertising_param 1275c3f60abcSHans Petter Selasky }, 1276c3f60abcSHans Petter Selasky { 1277c3f60abcSHans Petter Selasky "le_set_advertising_data", 1278c3f60abcSHans Petter Selasky "le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 \n" 1279c3f60abcSHans Petter Selasky "set LE device advertising packed data", 1280c3f60abcSHans Petter Selasky &le_set_advertising_data 1281c3f60abcSHans Petter Selasky }, 12821f5d883dSTakanori Watanabe { 12831f5d883dSTakanori Watanabe "le_read_buffer_size", 12841f5d883dSTakanori Watanabe "le_read_buffer_size [-v 1|2]\n" 12851f5d883dSTakanori Watanabe "Read the maximum size of ACL and ISO data packets", 12861f5d883dSTakanori Watanabe &le_read_buffer_size 12871f5d883dSTakanori Watanabe }, 12889287f06dSTakanori Watanabe { 12899287f06dSTakanori Watanabe "le_scan", 12909287f06dSTakanori Watanabe "le_scan [-a] [-v] [-n number_of_scans]\n" 12919287f06dSTakanori Watanabe "Do an LE scan", 12929287f06dSTakanori Watanabe &le_scan 12939287f06dSTakanori Watanabe }, 129411fb4bdbSTakanori Watanabe { 129511fb4bdbSTakanori Watanabe "le_read_white_list_size", 129611fb4bdbSTakanori Watanabe "le_read_white_list_size\n" 129711fb4bdbSTakanori Watanabe "Read total number of white list entries that can be stored", 129811fb4bdbSTakanori Watanabe &le_read_white_list_size 129911fb4bdbSTakanori Watanabe }, 130011fb4bdbSTakanori Watanabe { 130111fb4bdbSTakanori Watanabe "le_clear_white_list", 130211fb4bdbSTakanori Watanabe "le_clear_white_list\n" 130311fb4bdbSTakanori Watanabe "Clear the white list in the controller", 130411fb4bdbSTakanori Watanabe &le_clear_white_list 130511fb4bdbSTakanori Watanabe }, 130611fb4bdbSTakanori Watanabe { 130711fb4bdbSTakanori Watanabe "le_add_device_to_white_list", 130811fb4bdbSTakanori Watanabe "le_add_device_to_white_list\n" 130911fb4bdbSTakanori Watanabe "[-t public|random] -a address\n" 131011fb4bdbSTakanori Watanabe "Add device to the white list", 131111fb4bdbSTakanori Watanabe &le_add_device_to_white_list 131211fb4bdbSTakanori Watanabe }, 131311fb4bdbSTakanori Watanabe { 131411fb4bdbSTakanori Watanabe "le_remove_device_from_white_list", 131511fb4bdbSTakanori Watanabe "le_remove_device_from_white_list\n" 131611fb4bdbSTakanori Watanabe "[-t public|random] -a address\n" 131711fb4bdbSTakanori Watanabe "Remove device from the white list", 131811fb4bdbSTakanori Watanabe &le_remove_device_from_white_list 131911fb4bdbSTakanori Watanabe }, 13203ac41cceSTakanori Watanabe { 13213ac41cceSTakanori Watanabe "le_connect", 13223ac41cceSTakanori Watanabe "le_connect -a address [-t public|random] [-v]\n" 13233ac41cceSTakanori Watanabe "Connect to an LE device", 13243ac41cceSTakanori Watanabe &le_connect 13253ac41cceSTakanori Watanabe }, 1326*2b2c6d69STakanori Watanabe { 1327*2b2c6d69STakanori Watanabe "le_read_channel_map", 1328*2b2c6d69STakanori Watanabe "le_read_channel_map <connection_handle>\n" 1329*2b2c6d69STakanori Watanabe "Read the channel map for a connection", 1330*2b2c6d69STakanori Watanabe &le_read_channel_map 1331*2b2c6d69STakanori Watanabe }, 1332*2b2c6d69STakanori Watanabe { 1333*2b2c6d69STakanori Watanabe "le_read_remote_features", 1334*2b2c6d69STakanori Watanabe "le_read_remote_features <connection_handle>\n" 1335*2b2c6d69STakanori Watanabe "Read supported features for the device\n" 1336*2b2c6d69STakanori Watanabe "identified by the connection handle", 1337*2b2c6d69STakanori Watanabe &le_read_remote_features 1338*2b2c6d69STakanori Watanabe }, 1339bcff2d91STakanori Watanabe }; 1340