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); 722b2c6d69STakanori Watanabe static int le_read_channel_map(int s, int argc, char *argv[]); 732b2c6d69STakanori Watanabe static void handle_le_remote_features_event(ng_hci_event_pkt_t* e); 74*21056112STakanori Watanabe static int le_rand(int s, int argc, char *argv[]); 75bcff2d91STakanori Watanabe 769c3471faSMarcelo Araujo static int 779c3471faSMarcelo Araujo le_set_scan_param(int s, int argc, char *argv[]) 78bcff2d91STakanori Watanabe { 79bcff2d91STakanori Watanabe int type; 80bcff2d91STakanori Watanabe int interval; 81bcff2d91STakanori Watanabe int window; 82bcff2d91STakanori Watanabe int adrtype; 83bcff2d91STakanori Watanabe int policy; 84eb2aebeaSTakanori Watanabe int n; 85bcff2d91STakanori Watanabe 86bcff2d91STakanori Watanabe ng_hci_le_set_scan_parameters_cp cp; 87bcff2d91STakanori Watanabe ng_hci_le_set_scan_parameters_rp rp; 88bcff2d91STakanori Watanabe 899c3471faSMarcelo Araujo if (argc != 5) 90eb2aebeaSTakanori Watanabe return (USAGE); 91bcff2d91STakanori Watanabe 929c3471faSMarcelo Araujo if (strcmp(argv[0], "active") == 0) 93bcff2d91STakanori Watanabe type = 1; 949c3471faSMarcelo Araujo else if (strcmp(argv[0], "passive") == 0) 95bcff2d91STakanori Watanabe type = 0; 969c3471faSMarcelo Araujo else 97eb2aebeaSTakanori Watanabe return (USAGE); 98bcff2d91STakanori Watanabe 99bcff2d91STakanori Watanabe interval = (int)(atof(argv[1])/0.625); 100bcff2d91STakanori Watanabe interval = (interval < 4)? 4: interval; 101bcff2d91STakanori Watanabe window = (int)(atof(argv[2])/0.625); 102bcff2d91STakanori Watanabe window = (window < 4) ? 4 : interval; 103bcff2d91STakanori Watanabe 1049c3471faSMarcelo Araujo if (strcmp(argv[3], "public") == 0) 105bcff2d91STakanori Watanabe adrtype = 0; 10605e526fbSTakanori Watanabe else if (strcmp(argv[3], "random") == 0) 107bcff2d91STakanori Watanabe adrtype = 1; 1089c3471faSMarcelo Araujo else 109eb2aebeaSTakanori Watanabe return (USAGE); 110bcff2d91STakanori Watanabe 1119c3471faSMarcelo Araujo if (strcmp(argv[4], "all") == 0) 112bcff2d91STakanori Watanabe policy = 0; 1139c3471faSMarcelo Araujo else if (strcmp(argv[4], "whitelist") == 0) 114bcff2d91STakanori Watanabe policy = 1; 1159c3471faSMarcelo Araujo else 116eb2aebeaSTakanori Watanabe return (USAGE); 117bcff2d91STakanori Watanabe 118bcff2d91STakanori Watanabe cp.le_scan_type = type; 119bcff2d91STakanori Watanabe cp.le_scan_interval = interval; 120bcff2d91STakanori Watanabe cp.own_address_type = adrtype; 121bcff2d91STakanori Watanabe cp.le_scan_window = window; 122bcff2d91STakanori Watanabe cp.scanning_filter_policy = policy; 123bcff2d91STakanori Watanabe n = sizeof(rp); 124bcff2d91STakanori Watanabe 125eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 126eb2aebeaSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_PARAMETERS), 127eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 128eb2aebeaSTakanori Watanabe return (ERROR); 129eb2aebeaSTakanori Watanabe 130eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 131eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 132eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 133eb2aebeaSTakanori Watanabe return (FAILED); 134eb2aebeaSTakanori Watanabe } 135eb2aebeaSTakanori Watanabe 136eb2aebeaSTakanori Watanabe return (OK); 137bcff2d91STakanori Watanabe } 138bcff2d91STakanori Watanabe 1399c3471faSMarcelo Araujo static int 1409c3471faSMarcelo Araujo le_set_scan_enable(int s, int argc, char *argv[]) 141bcff2d91STakanori Watanabe { 142bcff2d91STakanori Watanabe ng_hci_le_set_scan_enable_cp cp; 143bcff2d91STakanori Watanabe ng_hci_le_set_scan_enable_rp rp; 144eb2aebeaSTakanori Watanabe int n, enable = 0; 145bcff2d91STakanori Watanabe 146bcff2d91STakanori Watanabe if (argc != 1) 147eb2aebeaSTakanori Watanabe return (USAGE); 148bcff2d91STakanori Watanabe 1499c3471faSMarcelo Araujo if (strcmp(argv[0], "enable") == 0) 150bcff2d91STakanori Watanabe enable = 1; 1519c3471faSMarcelo Araujo else if (strcmp(argv[0], "disable") != 0) 152eb2aebeaSTakanori Watanabe return (USAGE); 1539c3471faSMarcelo Araujo 154bcff2d91STakanori Watanabe n = sizeof(rp); 155bcff2d91STakanori Watanabe cp.le_scan_enable = enable; 156bcff2d91STakanori Watanabe cp.filter_duplicates = 0; 157eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 158bcff2d91STakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 159eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), 160eb2aebeaSTakanori Watanabe (void *)&rp, &n) == ERROR) 161eb2aebeaSTakanori Watanabe return (ERROR); 162bcff2d91STakanori Watanabe 163eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 164eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 165eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 166eb2aebeaSTakanori Watanabe return (FAILED); 167eb2aebeaSTakanori Watanabe } 168bcff2d91STakanori Watanabe 169eb2aebeaSTakanori Watanabe fprintf(stdout, "LE Scan: %s\n", 170eb2aebeaSTakanori Watanabe enable? "Enabled" : "Disabled"); 171eb2aebeaSTakanori Watanabe 172eb2aebeaSTakanori Watanabe return (OK); 173bcff2d91STakanori Watanabe } 1749c3471faSMarcelo Araujo 1759c3471faSMarcelo Araujo static int 1769c3471faSMarcelo Araujo parse_param(int argc, char *argv[], char *buf, int *len) 177bcff2d91STakanori Watanabe { 178bcff2d91STakanori Watanabe char *buflast = buf + (*len); 179bcff2d91STakanori Watanabe char *curbuf = buf; 180bcff2d91STakanori Watanabe char *token,*lenpos; 181bcff2d91STakanori Watanabe int ch; 182bcff2d91STakanori Watanabe int datalen; 183bcff2d91STakanori Watanabe uint16_t value; 184bcff2d91STakanori Watanabe optreset = 1; 185bcff2d91STakanori Watanabe optind = 0; 186bcff2d91STakanori Watanabe while ((ch = getopt(argc, argv , "n:f:u:")) != -1) { 187bcff2d91STakanori Watanabe switch(ch){ 188bcff2d91STakanori Watanabe case 'n': 189bcff2d91STakanori Watanabe datalen = strlen(optarg); 1909c3471faSMarcelo Araujo if ((curbuf + datalen + 2) >= buflast) 191bcff2d91STakanori Watanabe goto done; 192bcff2d91STakanori Watanabe curbuf[0] = datalen + 1; 193bcff2d91STakanori Watanabe curbuf[1] = 8; 194bcff2d91STakanori Watanabe curbuf += 2; 195bcff2d91STakanori Watanabe memcpy(curbuf, optarg, datalen); 196bcff2d91STakanori Watanabe curbuf += datalen; 197bcff2d91STakanori Watanabe break; 198bcff2d91STakanori Watanabe case 'f': 1999c3471faSMarcelo Araujo if (curbuf+3 > buflast) 200bcff2d91STakanori Watanabe goto done; 201bcff2d91STakanori Watanabe curbuf[0] = 2; 202bcff2d91STakanori Watanabe curbuf[1] = 1; 20332f32669SHans Petter Selasky curbuf[2] = (uint8_t)strtol(optarg, NULL, 16); 204bcff2d91STakanori Watanabe curbuf += 3; 205bcff2d91STakanori Watanabe break; 206bcff2d91STakanori Watanabe case 'u': 207bcff2d91STakanori Watanabe if ((buf+2) >= buflast) 208bcff2d91STakanori Watanabe goto done; 20932f32669SHans Petter Selasky lenpos = curbuf; 210bcff2d91STakanori Watanabe curbuf[1] = 2; 211bcff2d91STakanori Watanabe *lenpos = 1; 212bcff2d91STakanori Watanabe curbuf += 2; 213bcff2d91STakanori Watanabe while ((token = strsep(&optarg, ",")) != NULL) { 214bcff2d91STakanori Watanabe value = strtol(token, NULL, 16); 215bcff2d91STakanori Watanabe if ((curbuf+2) >= buflast) 216bcff2d91STakanori Watanabe break; 217bcff2d91STakanori Watanabe curbuf[0] = value &0xff; 218bcff2d91STakanori Watanabe curbuf[1] = (value>>8)&0xff; 219bcff2d91STakanori Watanabe curbuf += 2; 22032f32669SHans Petter Selasky *lenpos += 2; 221bcff2d91STakanori Watanabe } 222bcff2d91STakanori Watanabe 223bcff2d91STakanori Watanabe } 224bcff2d91STakanori Watanabe } 225bcff2d91STakanori Watanabe done: 226bcff2d91STakanori Watanabe *len = curbuf - buf; 227bcff2d91STakanori Watanabe 228eb2aebeaSTakanori Watanabe return (OK); 229bcff2d91STakanori Watanabe } 230bcff2d91STakanori Watanabe 2319c3471faSMarcelo Araujo static int 2329c3471faSMarcelo Araujo le_set_scan_response(int s, int argc, char *argv[]) 233bcff2d91STakanori Watanabe { 234bcff2d91STakanori Watanabe ng_hci_le_set_scan_response_data_cp cp; 235bcff2d91STakanori Watanabe ng_hci_le_set_scan_response_data_rp rp; 236bcff2d91STakanori Watanabe int n; 237bcff2d91STakanori Watanabe int len; 238bcff2d91STakanori Watanabe char buf[NG_HCI_ADVERTISING_DATA_SIZE]; 2399c3471faSMarcelo Araujo 240bcff2d91STakanori Watanabe len = sizeof(buf); 241bcff2d91STakanori Watanabe parse_param(argc, argv, buf, &len); 242bcff2d91STakanori Watanabe memset(cp.scan_response_data, 0, sizeof(cp.scan_response_data)); 243bcff2d91STakanori Watanabe cp.scan_response_data_length = len; 244bcff2d91STakanori Watanabe memcpy(cp.scan_response_data, buf, len); 245bcff2d91STakanori Watanabe n = sizeof(rp); 246eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 247bcff2d91STakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA), 248eb2aebeaSTakanori Watanabe (void *)&cp, sizeof(cp), 249eb2aebeaSTakanori Watanabe (void *)&rp, &n) == ERROR) 250eb2aebeaSTakanori Watanabe return (ERROR); 251bcff2d91STakanori Watanabe 252eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 253eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 254eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 255eb2aebeaSTakanori Watanabe return (FAILED); 256eb2aebeaSTakanori Watanabe } 257bcff2d91STakanori Watanabe 258eb2aebeaSTakanori Watanabe return (OK); 259bcff2d91STakanori Watanabe } 260bcff2d91STakanori Watanabe 2619c3471faSMarcelo Araujo static int 2629c3471faSMarcelo Araujo le_read_local_supported_features(int s, int argc ,char *argv[]) 263bcff2d91STakanori Watanabe { 264bcff2d91STakanori Watanabe ng_hci_le_read_local_supported_features_rp rp; 265bcff2d91STakanori Watanabe int n = sizeof(rp); 2669c3471faSMarcelo Araujo 267ea011491SHans Petter Selasky union { 268ea011491SHans Petter Selasky uint64_t raw; 269ea011491SHans Petter Selasky uint8_t octets[8]; 270ea011491SHans Petter Selasky } le_features; 271ea011491SHans Petter Selasky 272ea011491SHans Petter Selasky char buffer[2048]; 273ea011491SHans Petter Selasky 274ea011491SHans Petter Selasky if (hci_simple_request(s, 275bcff2d91STakanori Watanabe NG_HCI_OPCODE(NG_HCI_OGF_LE, 276bcff2d91STakanori Watanabe NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES), 277ea011491SHans Petter Selasky (void *)&rp, &n) == ERROR) 278ea011491SHans Petter Selasky return (ERROR); 2799c3471faSMarcelo Araujo 280ea011491SHans Petter Selasky if (rp.status != 0x00) { 281ea011491SHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 282ea011491SHans Petter Selasky hci_status2str(rp.status), rp.status); 283ea011491SHans Petter Selasky return (FAILED); 284ea011491SHans Petter Selasky } 285bcff2d91STakanori Watanabe 286ea011491SHans Petter Selasky le_features.raw = rp.le_features; 287ea011491SHans Petter Selasky 288ea011491SHans Petter Selasky fprintf(stdout, "LE Features: "); 289ea011491SHans Petter Selasky for(int i = 0; i < 8; i++) 290ea011491SHans Petter Selasky fprintf(stdout, " %#02x", le_features.octets[i]); 291ea011491SHans Petter Selasky fprintf(stdout, "\n%s\n", hci_le_features2str(le_features.octets, 292ea011491SHans Petter Selasky buffer, sizeof(buffer))); 293ea011491SHans Petter Selasky fprintf(stdout, "\n"); 294ea011491SHans Petter Selasky 295eb2aebeaSTakanori Watanabe return (OK); 296bcff2d91STakanori Watanabe } 2979c3471faSMarcelo Araujo 2989c3471faSMarcelo Araujo static int 29921eefd31SHans Petter Selasky le_read_supported_states(int s, int argc, char *argv[]) 300bcff2d91STakanori Watanabe { 30121eefd31SHans Petter Selasky ng_hci_le_read_supported_states_rp rp; 302bcff2d91STakanori Watanabe int n = sizeof(rp); 3039c3471faSMarcelo Araujo 30421eefd31SHans Petter Selasky if (hci_simple_request(s, NG_HCI_OPCODE( 3059c3471faSMarcelo Araujo NG_HCI_OGF_LE, 30621eefd31SHans Petter Selasky NG_HCI_OCF_LE_READ_SUPPORTED_STATES), 30721eefd31SHans Petter Selasky (void *)&rp, &n) == ERROR) 30821eefd31SHans Petter Selasky return (ERROR); 3099c3471faSMarcelo Araujo 31021eefd31SHans Petter Selasky if (rp.status != 0x00) { 31121eefd31SHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 31221eefd31SHans Petter Selasky hci_status2str(rp.status), rp.status); 31321eefd31SHans Petter Selasky return (FAILED); 31421eefd31SHans Petter Selasky } 315bcff2d91STakanori Watanabe 31621eefd31SHans Petter Selasky fprintf(stdout, "LE States: %jx\n", rp.le_states); 31721eefd31SHans Petter Selasky 31821eefd31SHans Petter Selasky return (OK); 319bcff2d91STakanori Watanabe } 320bcff2d91STakanori Watanabe 3219c3471faSMarcelo Araujo static int 3229c3471faSMarcelo Araujo set_le_event_mask(int s, uint64_t mask) 323bcff2d91STakanori Watanabe { 324bcff2d91STakanori Watanabe ng_hci_le_set_event_mask_cp semc; 325bcff2d91STakanori Watanabe ng_hci_le_set_event_mask_rp rp; 326eb2aebeaSTakanori Watanabe int i, n; 327bcff2d91STakanori Watanabe 328bcff2d91STakanori Watanabe n = sizeof(rp); 329bcff2d91STakanori Watanabe 330bcff2d91STakanori Watanabe for (i=0; i < NG_HCI_LE_EVENT_MASK_SIZE; i++) { 331bcff2d91STakanori Watanabe semc.event_mask[i] = mask&0xff; 332bcff2d91STakanori Watanabe mask >>= 8; 333bcff2d91STakanori Watanabe } 334eb2aebeaSTakanori Watanabe if(hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 3359c3471faSMarcelo Araujo NG_HCI_OCF_LE_SET_EVENT_MASK), 336eb2aebeaSTakanori Watanabe (void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR) 337eb2aebeaSTakanori Watanabe return (ERROR); 338bcff2d91STakanori Watanabe 339eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 340eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 341eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 342eb2aebeaSTakanori Watanabe return (FAILED); 343eb2aebeaSTakanori Watanabe } 344eb2aebeaSTakanori Watanabe 345eb2aebeaSTakanori Watanabe return (OK); 346bcff2d91STakanori Watanabe } 347bcff2d91STakanori Watanabe 3489c3471faSMarcelo Araujo static int 3499c3471faSMarcelo Araujo set_event_mask(int s, uint64_t mask) 350bcff2d91STakanori Watanabe { 351bcff2d91STakanori Watanabe ng_hci_set_event_mask_cp semc; 352bcff2d91STakanori Watanabe ng_hci_set_event_mask_rp rp; 353eb2aebeaSTakanori Watanabe int i, n; 354bcff2d91STakanori Watanabe 355bcff2d91STakanori Watanabe n = sizeof(rp); 356bcff2d91STakanori Watanabe 357bcff2d91STakanori Watanabe for (i=0; i < NG_HCI_EVENT_MASK_SIZE; i++) { 358bcff2d91STakanori Watanabe semc.event_mask[i] = mask&0xff; 359bcff2d91STakanori Watanabe mask >>= 8; 360bcff2d91STakanori Watanabe } 361eb2aebeaSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, 3629c3471faSMarcelo Araujo NG_HCI_OCF_SET_EVENT_MASK), 363eb2aebeaSTakanori Watanabe (void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR) 364eb2aebeaSTakanori Watanabe return (ERROR); 365bcff2d91STakanori Watanabe 366eb2aebeaSTakanori Watanabe if (rp.status != 0x00) { 367eb2aebeaSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 368eb2aebeaSTakanori Watanabe hci_status2str(rp.status), rp.status); 369eb2aebeaSTakanori Watanabe return (FAILED); 370eb2aebeaSTakanori Watanabe } 371eb2aebeaSTakanori Watanabe 372eb2aebeaSTakanori Watanabe return (OK); 373bcff2d91STakanori Watanabe } 374bcff2d91STakanori Watanabe 3759c3471faSMarcelo Araujo static 3769c3471faSMarcelo Araujo int le_enable(int s, int argc, char *argv[]) 377bcff2d91STakanori Watanabe { 378eb2aebeaSTakanori Watanabe int result; 379eb2aebeaSTakanori Watanabe 3809c3471faSMarcelo Araujo if (argc != 1) 381eb2aebeaSTakanori Watanabe return (USAGE); 382bcff2d91STakanori Watanabe 383bcff2d91STakanori Watanabe if (strcasecmp(argv[0], "enable") == 0) { 384eb2aebeaSTakanori Watanabe result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT | 385bcff2d91STakanori Watanabe NG_HCI_EVENT_MASK_LE); 386eb2aebeaSTakanori Watanabe if (result != OK) 387eb2aebeaSTakanori Watanabe return result; 388eb2aebeaSTakanori Watanabe result = set_le_event_mask(s, NG_HCI_LE_EVENT_MASK_ALL); 389eb2aebeaSTakanori Watanabe if (result == OK) { 390eb2aebeaSTakanori Watanabe fprintf(stdout, "LE enabled\n"); 391eb2aebeaSTakanori Watanabe return (OK); 392eb2aebeaSTakanori Watanabe } else 393eb2aebeaSTakanori Watanabe return result; 394eb2aebeaSTakanori Watanabe } else if (strcasecmp(argv[0], "disable") == 0) { 395eb2aebeaSTakanori Watanabe result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT); 396eb2aebeaSTakanori Watanabe if (result == OK) { 397eb2aebeaSTakanori Watanabe fprintf(stdout, "LE disabled\n"); 398eb2aebeaSTakanori Watanabe return (OK); 399eb2aebeaSTakanori Watanabe } else 400eb2aebeaSTakanori Watanabe return result; 401eb2aebeaSTakanori Watanabe } else 402eb2aebeaSTakanori Watanabe return (USAGE); 403bcff2d91STakanori Watanabe } 404bcff2d91STakanori Watanabe 405c3f60abcSHans Petter Selasky static int 406c3f60abcSHans Petter Selasky le_set_advertising_enable(int s, int argc, char *argv[]) 407c3f60abcSHans Petter Selasky { 408c3f60abcSHans Petter Selasky ng_hci_le_set_advertise_enable_cp cp; 409c3f60abcSHans Petter Selasky ng_hci_le_set_advertise_enable_rp rp; 410c3f60abcSHans Petter Selasky int n, enable = 0; 411c3f60abcSHans Petter Selasky 412c3f60abcSHans Petter Selasky if (argc != 1) 413c3f60abcSHans Petter Selasky return USAGE; 414c3f60abcSHans Petter Selasky 415c3f60abcSHans Petter Selasky if (strcmp(argv[0], "enable") == 0) 416c3f60abcSHans Petter Selasky enable = 1; 417c3f60abcSHans Petter Selasky else if (strcmp(argv[0], "disable") != 0) 418c3f60abcSHans Petter Selasky return USAGE; 419c3f60abcSHans Petter Selasky 420c3f60abcSHans Petter Selasky n = sizeof(rp); 421c3f60abcSHans Petter Selasky cp.advertising_enable = enable; 422c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 423c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE), 424c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 425c3f60abcSHans Petter Selasky return (ERROR); 426c3f60abcSHans Petter Selasky 427c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 428c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 429c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 430c3f60abcSHans Petter Selasky return (FAILED); 431c3f60abcSHans Petter Selasky } 432c3f60abcSHans Petter Selasky fprintf(stdout, "LE Advertising %s\n", (enable ? "enabled" : "disabled")); 433c3f60abcSHans Petter Selasky 434c3f60abcSHans Petter Selasky return (OK); 435c3f60abcSHans Petter Selasky } 436c3f60abcSHans Petter Selasky 437c3f60abcSHans Petter Selasky static int 438c3f60abcSHans Petter Selasky le_set_advertising_param(int s, int argc, char *argv[]) 439c3f60abcSHans Petter Selasky { 440c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_parameters_cp cp; 441c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_parameters_rp rp; 442c3f60abcSHans Petter Selasky 443c3f60abcSHans Petter Selasky int n, ch; 444c3f60abcSHans Petter Selasky 445c3f60abcSHans Petter Selasky cp.advertising_interval_min = 0x800; 446c3f60abcSHans Petter Selasky cp.advertising_interval_max = 0x800; 447c3f60abcSHans Petter Selasky cp.advertising_type = 0; 448c3f60abcSHans Petter Selasky cp.own_address_type = 0; 449c3f60abcSHans Petter Selasky cp.direct_address_type = 0; 450c3f60abcSHans Petter Selasky 451c3f60abcSHans Petter Selasky cp.advertising_channel_map = 7; 452c3f60abcSHans Petter Selasky cp.advertising_filter_policy = 0; 453c3f60abcSHans Petter Selasky 454c3f60abcSHans Petter Selasky optreset = 1; 455c3f60abcSHans Petter Selasky optind = 0; 456c3f60abcSHans Petter Selasky while ((ch = getopt(argc, argv , "m:M:t:o:p:a:c:f:")) != -1) { 457c3f60abcSHans Petter Selasky switch(ch) { 458c3f60abcSHans Petter Selasky case 'm': 459c3f60abcSHans Petter Selasky cp.advertising_interval_min = 460c3f60abcSHans Petter Selasky (uint16_t)(strtod(optarg, NULL)/0.625); 461c3f60abcSHans Petter Selasky break; 462c3f60abcSHans Petter Selasky case 'M': 463c3f60abcSHans Petter Selasky cp.advertising_interval_max = 464c3f60abcSHans Petter Selasky (uint16_t)(strtod(optarg, NULL)/0.625); 465c3f60abcSHans Petter Selasky break; 466c3f60abcSHans Petter Selasky case 't': 467c3f60abcSHans Petter Selasky cp.advertising_type = 468c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 469c3f60abcSHans Petter Selasky break; 470c3f60abcSHans Petter Selasky case 'o': 471c3f60abcSHans Petter Selasky cp.own_address_type = 472c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 473c3f60abcSHans Petter Selasky break; 474c3f60abcSHans Petter Selasky case 'p': 475c3f60abcSHans Petter Selasky cp.direct_address_type = 476c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 477c3f60abcSHans Petter Selasky break; 478c3f60abcSHans Petter Selasky case 'a': 479c3f60abcSHans Petter Selasky if (!bt_aton(optarg, &cp.direct_address)) { 480c3f60abcSHans Petter Selasky struct hostent *he = NULL; 481c3f60abcSHans Petter Selasky 482c3f60abcSHans Petter Selasky if ((he = bt_gethostbyname(optarg)) == NULL) 483c3f60abcSHans Petter Selasky return (USAGE); 484c3f60abcSHans Petter Selasky 485c3f60abcSHans Petter Selasky memcpy(&cp.direct_address, he->h_addr, sizeof(cp.direct_address)); 486c3f60abcSHans Petter Selasky } 487c3f60abcSHans Petter Selasky break; 488c3f60abcSHans Petter Selasky case 'c': 489c3f60abcSHans Petter Selasky cp.advertising_channel_map = 490c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 491c3f60abcSHans Petter Selasky break; 492c3f60abcSHans Petter Selasky case 'f': 493c3f60abcSHans Petter Selasky cp.advertising_filter_policy = 494c3f60abcSHans Petter Selasky (uint8_t)strtod(optarg, NULL); 495c3f60abcSHans Petter Selasky break; 496c3f60abcSHans Petter Selasky } 497c3f60abcSHans Petter Selasky } 498c3f60abcSHans Petter Selasky 499c3f60abcSHans Petter Selasky n = sizeof(rp); 500c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 501c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS), 502c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 503c3f60abcSHans Petter Selasky return (ERROR); 504c3f60abcSHans Petter Selasky 505c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 506c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 507c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 508c3f60abcSHans Petter Selasky return (FAILED); 509c3f60abcSHans Petter Selasky } 510c3f60abcSHans Petter Selasky 511c3f60abcSHans Petter Selasky return (OK); 512c3f60abcSHans Petter Selasky } 513c3f60abcSHans Petter Selasky 514c3f60abcSHans Petter Selasky static int 515c3f60abcSHans Petter Selasky le_read_advertising_channel_tx_power(int s, int argc, char *argv[]) 516c3f60abcSHans Petter Selasky { 517c3f60abcSHans Petter Selasky ng_hci_le_read_advertising_channel_tx_power_rp rp; 518c3f60abcSHans Petter Selasky int n; 519c3f60abcSHans Petter Selasky 520c3f60abcSHans Petter Selasky n = sizeof(rp); 521c3f60abcSHans Petter Selasky 522c3f60abcSHans Petter Selasky if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 523c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER), 524c3f60abcSHans Petter Selasky (void *)&rp, &n) == ERROR) 525c3f60abcSHans Petter Selasky return (ERROR); 526c3f60abcSHans Petter Selasky 527c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 528c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 529c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 530c3f60abcSHans Petter Selasky return (FAILED); 531c3f60abcSHans Petter Selasky } 532c3f60abcSHans Petter Selasky 533c3f60abcSHans Petter Selasky fprintf(stdout, "Advertising transmit power level: %d dBm\n", 534c3f60abcSHans Petter Selasky (int8_t)rp.transmit_power_level); 535c3f60abcSHans Petter Selasky 536c3f60abcSHans Petter Selasky return (OK); 537c3f60abcSHans Petter Selasky } 538c3f60abcSHans Petter Selasky 539c3f60abcSHans Petter Selasky static int 540c3f60abcSHans Petter Selasky le_set_advertising_data(int s, int argc, char *argv[]) 541c3f60abcSHans Petter Selasky { 542c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_data_cp cp; 543c3f60abcSHans Petter Selasky ng_hci_le_set_advertising_data_rp rp; 544c3f60abcSHans Petter Selasky int n, len; 545c3f60abcSHans Petter Selasky 546c3f60abcSHans Petter Selasky n = sizeof(rp); 547c3f60abcSHans Petter Selasky 548c3f60abcSHans Petter Selasky char buf[NG_HCI_ADVERTISING_DATA_SIZE]; 549c3f60abcSHans Petter Selasky 550c3f60abcSHans Petter Selasky len = sizeof(buf); 551c3f60abcSHans Petter Selasky parse_param(argc, argv, buf, &len); 552c3f60abcSHans Petter Selasky memset(cp.advertising_data, 0, sizeof(cp.advertising_data)); 553c3f60abcSHans Petter Selasky cp.advertising_data_length = len; 5547b2f84dbSHans Petter Selasky memcpy(cp.advertising_data, buf, len); 555c3f60abcSHans Petter Selasky 556c3f60abcSHans Petter Selasky if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 557c3f60abcSHans Petter Selasky NG_HCI_OCF_LE_SET_ADVERTISING_DATA), 558c3f60abcSHans Petter Selasky (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 559c3f60abcSHans Petter Selasky return (ERROR); 560c3f60abcSHans Petter Selasky 561c3f60abcSHans Petter Selasky if (rp.status != 0x00) { 562c3f60abcSHans Petter Selasky fprintf(stdout, "Status: %s [%#02x]\n", 563c3f60abcSHans Petter Selasky hci_status2str(rp.status), rp.status); 564c3f60abcSHans Petter Selasky return (FAILED); 565c3f60abcSHans Petter Selasky } 566c3f60abcSHans Petter Selasky 567c3f60abcSHans Petter Selasky return (OK); 568c3f60abcSHans Petter Selasky } 5691f5d883dSTakanori Watanabe static int 5701f5d883dSTakanori Watanabe le_read_buffer_size(int s, int argc, char *argv[]) 5711f5d883dSTakanori Watanabe { 5721f5d883dSTakanori Watanabe union { 5731f5d883dSTakanori Watanabe ng_hci_le_read_buffer_size_rp v1; 5741f5d883dSTakanori Watanabe ng_hci_le_read_buffer_size_rp_v2 v2; 5751f5d883dSTakanori Watanabe } rp; 5761f5d883dSTakanori Watanabe 5771f5d883dSTakanori Watanabe int n, ch; 5781f5d883dSTakanori Watanabe uint8_t v; 5791f5d883dSTakanori Watanabe uint16_t cmd; 5801f5d883dSTakanori Watanabe 5811f5d883dSTakanori Watanabe optreset = 1; 5821f5d883dSTakanori Watanabe optind = 0; 5831f5d883dSTakanori Watanabe 5841f5d883dSTakanori Watanabe /* Default to version 1*/ 5851f5d883dSTakanori Watanabe v = 1; 5861f5d883dSTakanori Watanabe cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE; 5871f5d883dSTakanori Watanabe 5881f5d883dSTakanori Watanabe while ((ch = getopt(argc, argv , "v:")) != -1) { 5891f5d883dSTakanori Watanabe switch(ch) { 5901f5d883dSTakanori Watanabe case 'v': 5911f5d883dSTakanori Watanabe v = (uint8_t)strtol(optarg, NULL, 16); 5921f5d883dSTakanori Watanabe if (v == 2) 5931f5d883dSTakanori Watanabe cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE_V2; 5941f5d883dSTakanori Watanabe else if (v > 2) 5951f5d883dSTakanori Watanabe return (USAGE); 5961f5d883dSTakanori Watanabe break; 5971f5d883dSTakanori Watanabe default: 5981f5d883dSTakanori Watanabe v = 1; 5991f5d883dSTakanori Watanabe } 6001f5d883dSTakanori Watanabe } 6011f5d883dSTakanori Watanabe 6021f5d883dSTakanori Watanabe n = sizeof(rp); 6031f5d883dSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, cmd), 6041f5d883dSTakanori Watanabe (void *)&rp, &n) == ERROR) 6051f5d883dSTakanori Watanabe return (ERROR); 6061f5d883dSTakanori Watanabe 6071f5d883dSTakanori Watanabe if (rp.v1.status != 0x00) { 6081f5d883dSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 6091f5d883dSTakanori Watanabe hci_status2str(rp.v1.status), rp.v1.status); 6101f5d883dSTakanori Watanabe return (FAILED); 6111f5d883dSTakanori Watanabe } 6121f5d883dSTakanori Watanabe 6131f5d883dSTakanori Watanabe fprintf(stdout, "ACL data packet length: %d\n", 6141f5d883dSTakanori Watanabe rp.v1.hc_le_data_packet_length); 6151f5d883dSTakanori Watanabe fprintf(stdout, "Number of ACL data packets: %d\n", 6161f5d883dSTakanori Watanabe rp.v1.hc_total_num_le_data_packets); 6171f5d883dSTakanori Watanabe 6181f5d883dSTakanori Watanabe if (v == 2) { 6191f5d883dSTakanori Watanabe fprintf(stdout, "ISO data packet length: %d\n", 6201f5d883dSTakanori Watanabe rp.v2.hc_iso_data_packet_length); 6211f5d883dSTakanori Watanabe fprintf(stdout, "Number of ISO data packets: %d\n", 6221f5d883dSTakanori Watanabe rp.v2.hc_total_num_iso_data_packets); 6231f5d883dSTakanori Watanabe } 6241f5d883dSTakanori Watanabe 6251f5d883dSTakanori Watanabe return (OK); 6261f5d883dSTakanori Watanabe } 627c3f60abcSHans Petter Selasky 6289287f06dSTakanori Watanabe static int 6299287f06dSTakanori Watanabe le_scan(int s, int argc, char *argv[]) 6309287f06dSTakanori Watanabe { 6319287f06dSTakanori Watanabe int n, bufsize, scancount, numscans; 6329287f06dSTakanori Watanabe bool verbose; 6339287f06dSTakanori Watanabe uint8_t active = 0; 6349287f06dSTakanori Watanabe char ch; 6359287f06dSTakanori Watanabe 6369287f06dSTakanori Watanabe char b[512]; 6379287f06dSTakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 6389287f06dSTakanori Watanabe 6399287f06dSTakanori Watanabe ng_hci_le_set_scan_parameters_cp scan_param_cp; 6409287f06dSTakanori Watanabe ng_hci_le_set_scan_parameters_rp scan_param_rp; 6419287f06dSTakanori Watanabe 6429287f06dSTakanori Watanabe ng_hci_le_set_scan_enable_cp scan_enable_cp; 6439287f06dSTakanori Watanabe ng_hci_le_set_scan_enable_rp scan_enable_rp; 6449287f06dSTakanori Watanabe 6459287f06dSTakanori Watanabe optreset = 1; 6469287f06dSTakanori Watanabe optind = 0; 6479287f06dSTakanori Watanabe verbose = false; 6489287f06dSTakanori Watanabe numscans = 1; 6499287f06dSTakanori Watanabe 6509287f06dSTakanori Watanabe while ((ch = getopt(argc, argv , "an:v")) != -1) { 6519287f06dSTakanori Watanabe switch(ch) { 6529287f06dSTakanori Watanabe case 'a': 6539287f06dSTakanori Watanabe active = 1; 6549287f06dSTakanori Watanabe break; 6559287f06dSTakanori Watanabe case 'n': 6569287f06dSTakanori Watanabe numscans = (uint8_t)strtol(optarg, NULL, 10); 6579287f06dSTakanori Watanabe break; 6589287f06dSTakanori Watanabe case 'v': 6599287f06dSTakanori Watanabe verbose = true; 6609287f06dSTakanori Watanabe break; 6619287f06dSTakanori Watanabe } 6629287f06dSTakanori Watanabe } 6639287f06dSTakanori Watanabe 6649287f06dSTakanori Watanabe scan_param_cp.le_scan_type = active; 6659287f06dSTakanori Watanabe scan_param_cp.le_scan_interval = (uint16_t)(100/0.625); 6669287f06dSTakanori Watanabe scan_param_cp.le_scan_window = (uint16_t)(50/0.625); 6679287f06dSTakanori Watanabe /* Address type public */ 6689287f06dSTakanori Watanabe scan_param_cp.own_address_type = 0; 6699287f06dSTakanori Watanabe /* 'All' filter policy */ 6709287f06dSTakanori Watanabe scan_param_cp.scanning_filter_policy = 0; 6719287f06dSTakanori Watanabe n = sizeof(scan_param_rp); 6729287f06dSTakanori Watanabe 6739287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 6749287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_PARAMETERS), 6759287f06dSTakanori Watanabe (void *)&scan_param_cp, sizeof(scan_param_cp), 6769287f06dSTakanori Watanabe (void *)&scan_param_rp, &n) == ERROR) 6779287f06dSTakanori Watanabe return (ERROR); 6789287f06dSTakanori Watanabe 6799287f06dSTakanori Watanabe if (scan_param_rp.status != 0x00) { 6809287f06dSTakanori Watanabe fprintf(stdout, "LE_Set_Scan_Parameters failed. Status: %s [%#02x]\n", 6819287f06dSTakanori Watanabe hci_status2str(scan_param_rp.status), 6829287f06dSTakanori Watanabe scan_param_rp.status); 6839287f06dSTakanori Watanabe return (FAILED); 6849287f06dSTakanori Watanabe } 6859287f06dSTakanori Watanabe 6869287f06dSTakanori Watanabe /* Enable scanning */ 6879287f06dSTakanori Watanabe n = sizeof(scan_enable_rp); 6889287f06dSTakanori Watanabe scan_enable_cp.le_scan_enable = 1; 6899287f06dSTakanori Watanabe scan_enable_cp.filter_duplicates = 1; 6909287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 6919287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 6929287f06dSTakanori Watanabe (void *)&scan_enable_cp, sizeof(scan_enable_cp), 6939287f06dSTakanori Watanabe (void *)&scan_enable_rp, &n) == ERROR) 6949287f06dSTakanori Watanabe return (ERROR); 6959287f06dSTakanori Watanabe 6969287f06dSTakanori Watanabe if (scan_enable_rp.status != 0x00) { 6979287f06dSTakanori Watanabe fprintf(stdout, "LE_Scan_Enable enable failed. Status: %s [%#02x]\n", 6989287f06dSTakanori Watanabe hci_status2str(scan_enable_rp.status), 6999287f06dSTakanori Watanabe scan_enable_rp.status); 7009287f06dSTakanori Watanabe return (FAILED); 7019287f06dSTakanori Watanabe } 7029287f06dSTakanori Watanabe 7039287f06dSTakanori Watanabe scancount = 0; 7049287f06dSTakanori Watanabe while (scancount < numscans) { 7059287f06dSTakanori Watanabe /* wait for scan events */ 7069287f06dSTakanori Watanabe bufsize = sizeof(b); 7079287f06dSTakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 7089287f06dSTakanori Watanabe return (ERROR); 7099287f06dSTakanori Watanabe } 7109287f06dSTakanori Watanabe 7119287f06dSTakanori Watanabe if (bufsize < sizeof(*e)) { 7129287f06dSTakanori Watanabe errno = EIO; 7139287f06dSTakanori Watanabe return (ERROR); 7149287f06dSTakanori Watanabe } 7159287f06dSTakanori Watanabe scancount++; 7169287f06dSTakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 7179287f06dSTakanori Watanabe fprintf(stdout, "Scan %d\n", scancount); 7189287f06dSTakanori Watanabe handle_le_event(e, verbose); 7199287f06dSTakanori Watanabe } 7209287f06dSTakanori Watanabe } 7219287f06dSTakanori Watanabe 7229287f06dSTakanori Watanabe fprintf(stdout, "Scan complete\n"); 7239287f06dSTakanori Watanabe 7249287f06dSTakanori Watanabe /* Disable scanning */ 7259287f06dSTakanori Watanabe n = sizeof(scan_enable_rp); 7269287f06dSTakanori Watanabe scan_enable_cp.le_scan_enable = 0; 7279287f06dSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 7289287f06dSTakanori Watanabe NG_HCI_OCF_LE_SET_SCAN_ENABLE), 7299287f06dSTakanori Watanabe (void *)&scan_enable_cp, sizeof(scan_enable_cp), 7309287f06dSTakanori Watanabe (void *)&scan_enable_rp, &n) == ERROR) 7319287f06dSTakanori Watanabe return (ERROR); 7329287f06dSTakanori Watanabe 7339287f06dSTakanori Watanabe if (scan_enable_rp.status != 0x00) { 7349287f06dSTakanori Watanabe fprintf(stdout, "LE_Scan_Enable disable failed. Status: %s [%#02x]\n", 7359287f06dSTakanori Watanabe hci_status2str(scan_enable_rp.status), 7369287f06dSTakanori Watanabe scan_enable_rp.status); 7379287f06dSTakanori Watanabe return (FAILED); 7389287f06dSTakanori Watanabe } 7399287f06dSTakanori Watanabe 7409287f06dSTakanori Watanabe return (OK); 7419287f06dSTakanori Watanabe } 7429287f06dSTakanori Watanabe 7439287f06dSTakanori Watanabe static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose) 7449287f06dSTakanori Watanabe { 7459287f06dSTakanori Watanabe int rc; 7469287f06dSTakanori Watanabe ng_hci_le_ep *leer = 7479287f06dSTakanori Watanabe (ng_hci_le_ep *)(e + 1); 7489287f06dSTakanori Watanabe ng_hci_le_advertising_report_ep *advrep = 7499287f06dSTakanori Watanabe (ng_hci_le_advertising_report_ep *)(leer + 1); 7509287f06dSTakanori Watanabe ng_hci_le_advreport *reports = 7519287f06dSTakanori Watanabe (ng_hci_le_advreport *)(advrep + 1); 7529287f06dSTakanori Watanabe 7539287f06dSTakanori Watanabe if (leer->subevent_code == NG_HCI_LEEV_ADVREP) { 7549287f06dSTakanori Watanabe fprintf(stdout, "Scan result, num_reports: %d\n", 7559287f06dSTakanori Watanabe advrep->num_reports); 7569287f06dSTakanori Watanabe for(rc = 0; rc < advrep->num_reports; rc++) { 7579287f06dSTakanori Watanabe uint8_t length = (uint8_t)reports[rc].length_data; 7589287f06dSTakanori Watanabe fprintf(stdout, "\tBD_ADDR %s \n", 7599287f06dSTakanori Watanabe hci_bdaddr2str(&reports[rc].bdaddr)); 7609287f06dSTakanori Watanabe fprintf(stdout, "\tAddress type: %s\n", 7619287f06dSTakanori Watanabe hci_addrtype2str(reports[rc].addr_type)); 7629287f06dSTakanori Watanabe if (length > 0 && verbose) { 7639287f06dSTakanori Watanabe dump_adv_data(length, reports[rc].data); 7649287f06dSTakanori Watanabe print_adv_data(length, reports[rc].data); 7659287f06dSTakanori Watanabe fprintf(stdout, 7669287f06dSTakanori Watanabe "\tRSSI: %d dBm\n", 7679287f06dSTakanori Watanabe (int8_t)reports[rc].data[length]); 7689287f06dSTakanori Watanabe fprintf(stdout, "\n"); 7699287f06dSTakanori Watanabe } 7709287f06dSTakanori Watanabe } 7719287f06dSTakanori Watanabe } 7729287f06dSTakanori Watanabe } 7739287f06dSTakanori Watanabe 77411fb4bdbSTakanori Watanabe static int 77511fb4bdbSTakanori Watanabe le_read_white_list_size(int s, int argc, char *argv[]) 77611fb4bdbSTakanori Watanabe { 77711fb4bdbSTakanori Watanabe ng_hci_le_read_white_list_size_rp rp; 77811fb4bdbSTakanori Watanabe int n; 77911fb4bdbSTakanori Watanabe 78011fb4bdbSTakanori Watanabe n = sizeof(rp); 78111fb4bdbSTakanori Watanabe 78211fb4bdbSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 78311fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE), 78411fb4bdbSTakanori Watanabe (void *)&rp, &n) == ERROR) 78511fb4bdbSTakanori Watanabe return (ERROR); 78611fb4bdbSTakanori Watanabe 78711fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 78811fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 78911fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 79011fb4bdbSTakanori Watanabe return (FAILED); 79111fb4bdbSTakanori Watanabe } 79211fb4bdbSTakanori Watanabe 79311fb4bdbSTakanori Watanabe fprintf(stdout, "White list size: %d\n", 79411fb4bdbSTakanori Watanabe (uint8_t)rp.white_list_size); 79511fb4bdbSTakanori Watanabe 79611fb4bdbSTakanori Watanabe return (OK); 79711fb4bdbSTakanori Watanabe } 79811fb4bdbSTakanori Watanabe 79911fb4bdbSTakanori Watanabe static int 80011fb4bdbSTakanori Watanabe le_clear_white_list(int s, int argc, char *argv[]) 80111fb4bdbSTakanori Watanabe { 80211fb4bdbSTakanori Watanabe ng_hci_le_clear_white_list_rp rp; 80311fb4bdbSTakanori Watanabe int n; 80411fb4bdbSTakanori Watanabe 80511fb4bdbSTakanori Watanabe n = sizeof(rp); 80611fb4bdbSTakanori Watanabe 80711fb4bdbSTakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 80811fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_CLEAR_WHITE_LIST), 80911fb4bdbSTakanori Watanabe (void *)&rp, &n) == ERROR) 81011fb4bdbSTakanori Watanabe return (ERROR); 81111fb4bdbSTakanori Watanabe 81211fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 81311fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 81411fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 81511fb4bdbSTakanori Watanabe return (FAILED); 81611fb4bdbSTakanori Watanabe } 81711fb4bdbSTakanori Watanabe 81811fb4bdbSTakanori Watanabe fprintf(stdout, "White list cleared\n"); 81911fb4bdbSTakanori Watanabe 82011fb4bdbSTakanori Watanabe return (OK); 82111fb4bdbSTakanori Watanabe } 82211fb4bdbSTakanori Watanabe 82311fb4bdbSTakanori Watanabe static int 82411fb4bdbSTakanori Watanabe le_add_device_to_white_list(int s, int argc, char *argv[]) 82511fb4bdbSTakanori Watanabe { 82611fb4bdbSTakanori Watanabe ng_hci_le_add_device_to_white_list_cp cp; 82711fb4bdbSTakanori Watanabe ng_hci_le_add_device_to_white_list_rp rp; 82811fb4bdbSTakanori Watanabe int n; 82911fb4bdbSTakanori Watanabe char ch; 83011fb4bdbSTakanori Watanabe optreset = 1; 83111fb4bdbSTakanori Watanabe optind = 0; 83211fb4bdbSTakanori Watanabe bool addr_set = false; 83311fb4bdbSTakanori Watanabe 83411fb4bdbSTakanori Watanabe n = sizeof(rp); 83511fb4bdbSTakanori Watanabe 83611fb4bdbSTakanori Watanabe cp.address_type = 0x00; 83711fb4bdbSTakanori Watanabe 83811fb4bdbSTakanori Watanabe while ((ch = getopt(argc, argv , "t:a:")) != -1) { 83911fb4bdbSTakanori Watanabe switch(ch) { 84011fb4bdbSTakanori Watanabe case 't': 84111fb4bdbSTakanori Watanabe if (strcmp(optarg, "public") == 0) 84211fb4bdbSTakanori Watanabe cp.address_type = 0x00; 84311fb4bdbSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 84411fb4bdbSTakanori Watanabe cp.address_type = 0x01; 84511fb4bdbSTakanori Watanabe else 84611fb4bdbSTakanori Watanabe return (USAGE); 84711fb4bdbSTakanori Watanabe break; 84811fb4bdbSTakanori Watanabe case 'a': 84911fb4bdbSTakanori Watanabe addr_set = true; 85011fb4bdbSTakanori Watanabe if (!bt_aton(optarg, &cp.address)) { 85111fb4bdbSTakanori Watanabe struct hostent *he = NULL; 85211fb4bdbSTakanori Watanabe 85311fb4bdbSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 85411fb4bdbSTakanori Watanabe return (USAGE); 85511fb4bdbSTakanori Watanabe 85611fb4bdbSTakanori Watanabe memcpy(&cp.address, he->h_addr, 85711fb4bdbSTakanori Watanabe sizeof(cp.address)); 85811fb4bdbSTakanori Watanabe } 85911fb4bdbSTakanori Watanabe break; 86011fb4bdbSTakanori Watanabe } 86111fb4bdbSTakanori Watanabe } 86211fb4bdbSTakanori Watanabe 86311fb4bdbSTakanori Watanabe if (addr_set == false) 86411fb4bdbSTakanori Watanabe return (USAGE); 86511fb4bdbSTakanori Watanabe 86611fb4bdbSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 86711fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST), 86811fb4bdbSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 86911fb4bdbSTakanori Watanabe return (ERROR); 87011fb4bdbSTakanori Watanabe 87111fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 87211fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 87311fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 87411fb4bdbSTakanori Watanabe return (FAILED); 87511fb4bdbSTakanori Watanabe } 87611fb4bdbSTakanori Watanabe 87711fb4bdbSTakanori Watanabe fprintf(stdout, "Address added to white list\n"); 87811fb4bdbSTakanori Watanabe 87911fb4bdbSTakanori Watanabe return (OK); 88011fb4bdbSTakanori Watanabe } 88111fb4bdbSTakanori Watanabe 88211fb4bdbSTakanori Watanabe static int 88311fb4bdbSTakanori Watanabe le_remove_device_from_white_list(int s, int argc, char *argv[]) 88411fb4bdbSTakanori Watanabe { 88511fb4bdbSTakanori Watanabe ng_hci_le_remove_device_from_white_list_cp cp; 88611fb4bdbSTakanori Watanabe ng_hci_le_remove_device_from_white_list_rp rp; 88711fb4bdbSTakanori Watanabe int n; 88811fb4bdbSTakanori Watanabe char ch; 88911fb4bdbSTakanori Watanabe optreset = 1; 89011fb4bdbSTakanori Watanabe optind = 0; 89111fb4bdbSTakanori Watanabe bool addr_set = false; 89211fb4bdbSTakanori Watanabe 89311fb4bdbSTakanori Watanabe n = sizeof(rp); 89411fb4bdbSTakanori Watanabe 89511fb4bdbSTakanori Watanabe cp.address_type = 0x00; 89611fb4bdbSTakanori Watanabe 89711fb4bdbSTakanori Watanabe while ((ch = getopt(argc, argv , "t:a:")) != -1) { 89811fb4bdbSTakanori Watanabe switch(ch) { 89911fb4bdbSTakanori Watanabe case 't': 90011fb4bdbSTakanori Watanabe if (strcmp(optarg, "public") == 0) 90111fb4bdbSTakanori Watanabe cp.address_type = 0x00; 90211fb4bdbSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 90311fb4bdbSTakanori Watanabe cp.address_type = 0x01; 90411fb4bdbSTakanori Watanabe else 90511fb4bdbSTakanori Watanabe return (USAGE); 90611fb4bdbSTakanori Watanabe break; 90711fb4bdbSTakanori Watanabe case 'a': 90811fb4bdbSTakanori Watanabe addr_set = true; 90911fb4bdbSTakanori Watanabe if (!bt_aton(optarg, &cp.address)) { 91011fb4bdbSTakanori Watanabe struct hostent *he = NULL; 91111fb4bdbSTakanori Watanabe 91211fb4bdbSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 91311fb4bdbSTakanori Watanabe return (USAGE); 91411fb4bdbSTakanori Watanabe 91511fb4bdbSTakanori Watanabe memcpy(&cp.address, he->h_addr, 91611fb4bdbSTakanori Watanabe sizeof(cp.address)); 91711fb4bdbSTakanori Watanabe } 91811fb4bdbSTakanori Watanabe break; 91911fb4bdbSTakanori Watanabe } 92011fb4bdbSTakanori Watanabe } 92111fb4bdbSTakanori Watanabe 92211fb4bdbSTakanori Watanabe if (addr_set == false) 92311fb4bdbSTakanori Watanabe return (USAGE); 92411fb4bdbSTakanori Watanabe 92511fb4bdbSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 92611fb4bdbSTakanori Watanabe NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST), 92711fb4bdbSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 92811fb4bdbSTakanori Watanabe return (ERROR); 92911fb4bdbSTakanori Watanabe 93011fb4bdbSTakanori Watanabe if (rp.status != 0x00) { 93111fb4bdbSTakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 93211fb4bdbSTakanori Watanabe hci_status2str(rp.status), rp.status); 93311fb4bdbSTakanori Watanabe return (FAILED); 93411fb4bdbSTakanori Watanabe } 93511fb4bdbSTakanori Watanabe 93611fb4bdbSTakanori Watanabe fprintf(stdout, "Address removed from white list\n"); 93711fb4bdbSTakanori Watanabe 93811fb4bdbSTakanori Watanabe return (OK); 93911fb4bdbSTakanori Watanabe } 94011fb4bdbSTakanori Watanabe 9413ac41cceSTakanori Watanabe static int 9423ac41cceSTakanori Watanabe le_connect(int s, int argc, char *argv[]) 9433ac41cceSTakanori Watanabe { 9443ac41cceSTakanori Watanabe ng_hci_le_create_connection_cp cp; 9453ac41cceSTakanori Watanabe ng_hci_status_rp rp; 9463ac41cceSTakanori Watanabe char b[512]; 9473ac41cceSTakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 9483ac41cceSTakanori Watanabe 9493ac41cceSTakanori Watanabe int n, scancount, bufsize; 9503ac41cceSTakanori Watanabe char ch; 9513ac41cceSTakanori Watanabe bool addr_set = false; 9523ac41cceSTakanori Watanabe bool verbose = false; 9533ac41cceSTakanori Watanabe 9543ac41cceSTakanori Watanabe optreset = 1; 9553ac41cceSTakanori Watanabe optind = 0; 9563ac41cceSTakanori Watanabe 9573ac41cceSTakanori Watanabe /* minimal scan interval (2.5ms) */ 9583ac41cceSTakanori Watanabe cp.scan_interval = htole16(4); 9593ac41cceSTakanori Watanabe cp.scan_window = htole16(4); 9603ac41cceSTakanori Watanabe 9613ac41cceSTakanori Watanabe /* Don't use the whitelist */ 9623ac41cceSTakanori Watanabe cp.filter_policy = 0x00; 9633ac41cceSTakanori Watanabe 9643ac41cceSTakanori Watanabe /* Default to public peer address */ 9653ac41cceSTakanori Watanabe cp.peer_addr_type = 0x00; 9663ac41cceSTakanori Watanabe 9673ac41cceSTakanori Watanabe /* Own address type public */ 9683ac41cceSTakanori Watanabe cp.own_address_type = 0x00; 9693ac41cceSTakanori Watanabe 9703ac41cceSTakanori Watanabe /* 18.75ms min connection interval */ 9713ac41cceSTakanori Watanabe cp.conn_interval_min = htole16(0x000F); 9723ac41cceSTakanori Watanabe /* 18.75ms max connection interval */ 9733ac41cceSTakanori Watanabe cp.conn_interval_max = htole16(0x000F); 9743ac41cceSTakanori Watanabe 9753ac41cceSTakanori Watanabe /* 0 events connection latency */ 9763ac41cceSTakanori Watanabe cp.conn_latency = htole16(0x0000); 9773ac41cceSTakanori Watanabe 9783ac41cceSTakanori Watanabe /* 32s supervision timeout */ 9793ac41cceSTakanori Watanabe cp.supervision_timeout = htole16(0x0C80); 9803ac41cceSTakanori Watanabe 9813ac41cceSTakanori Watanabe /* Min CE Length 0.625 ms */ 9823ac41cceSTakanori Watanabe cp.min_ce_length = htole16(1); 9833ac41cceSTakanori Watanabe /* Max CE Length 0.625 ms */ 9843ac41cceSTakanori Watanabe cp.max_ce_length = htole16(1); 9853ac41cceSTakanori Watanabe 9863ac41cceSTakanori Watanabe while ((ch = getopt(argc, argv , "a:t:v")) != -1) { 9873ac41cceSTakanori Watanabe switch(ch) { 9883ac41cceSTakanori Watanabe case 't': 9893ac41cceSTakanori Watanabe if (strcmp(optarg, "public") == 0) 9903ac41cceSTakanori Watanabe cp.peer_addr_type = 0x00; 9913ac41cceSTakanori Watanabe else if (strcmp(optarg, "random") == 0) 9923ac41cceSTakanori Watanabe cp.peer_addr_type = 0x01; 9933ac41cceSTakanori Watanabe else 9943ac41cceSTakanori Watanabe return (USAGE); 9953ac41cceSTakanori Watanabe break; 9963ac41cceSTakanori Watanabe case 'a': 9973ac41cceSTakanori Watanabe addr_set = true; 9983ac41cceSTakanori Watanabe if (!bt_aton(optarg, &cp.peer_addr)) { 9993ac41cceSTakanori Watanabe struct hostent *he = NULL; 10003ac41cceSTakanori Watanabe 10013ac41cceSTakanori Watanabe if ((he = bt_gethostbyname(optarg)) == NULL) 10023ac41cceSTakanori Watanabe return (USAGE); 10033ac41cceSTakanori Watanabe 10043ac41cceSTakanori Watanabe memcpy(&cp.peer_addr, he->h_addr, 10053ac41cceSTakanori Watanabe sizeof(cp.peer_addr)); 10063ac41cceSTakanori Watanabe } 10073ac41cceSTakanori Watanabe break; 10083ac41cceSTakanori Watanabe case 'v': 10093ac41cceSTakanori Watanabe verbose = true; 10103ac41cceSTakanori Watanabe break; 10113ac41cceSTakanori Watanabe } 10123ac41cceSTakanori Watanabe } 10133ac41cceSTakanori Watanabe 10143ac41cceSTakanori Watanabe if (addr_set == false) 10153ac41cceSTakanori Watanabe return (USAGE); 10163ac41cceSTakanori Watanabe 10173ac41cceSTakanori Watanabe n = sizeof(rp); 10183ac41cceSTakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 10193ac41cceSTakanori Watanabe NG_HCI_OCF_LE_CREATE_CONNECTION), 10203ac41cceSTakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 10213ac41cceSTakanori Watanabe return (ERROR); 10223ac41cceSTakanori Watanabe 10233ac41cceSTakanori Watanabe if (rp.status != 0x00) { 10243ac41cceSTakanori Watanabe fprintf(stdout, 10253ac41cceSTakanori Watanabe "Create connection failed. Status: %s [%#02x]\n", 10263ac41cceSTakanori Watanabe hci_status2str(rp.status), rp.status); 10273ac41cceSTakanori Watanabe return (FAILED); 10283ac41cceSTakanori Watanabe } 10293ac41cceSTakanori Watanabe 10303ac41cceSTakanori Watanabe scancount = 0; 10313ac41cceSTakanori Watanabe while (scancount < 3) { 10323ac41cceSTakanori Watanabe /* wait for connection events */ 10333ac41cceSTakanori Watanabe bufsize = sizeof(b); 10343ac41cceSTakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 10353ac41cceSTakanori Watanabe return (ERROR); 10363ac41cceSTakanori Watanabe } 10373ac41cceSTakanori Watanabe 10383ac41cceSTakanori Watanabe if (bufsize < sizeof(*e)) { 10393ac41cceSTakanori Watanabe errno = EIO; 10403ac41cceSTakanori Watanabe return (ERROR); 10413ac41cceSTakanori Watanabe } 10423ac41cceSTakanori Watanabe scancount++; 10433ac41cceSTakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 10443ac41cceSTakanori Watanabe handle_le_connection_event(e, verbose); 10453ac41cceSTakanori Watanabe break; 10463ac41cceSTakanori Watanabe } 10473ac41cceSTakanori Watanabe } 10483ac41cceSTakanori Watanabe 10493ac41cceSTakanori Watanabe return (OK); 10503ac41cceSTakanori Watanabe } 10513ac41cceSTakanori Watanabe 10523ac41cceSTakanori Watanabe static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose) 10533ac41cceSTakanori Watanabe { 10543ac41cceSTakanori Watanabe ng_hci_le_ep *ev_pkt; 10553ac41cceSTakanori Watanabe ng_hci_le_connection_complete_ep *conn_event; 10563ac41cceSTakanori Watanabe 10573ac41cceSTakanori Watanabe ev_pkt = (ng_hci_le_ep *)(e + 1); 10583ac41cceSTakanori Watanabe 10593ac41cceSTakanori Watanabe if (ev_pkt->subevent_code == NG_HCI_LEEV_CON_COMPL) { 10603ac41cceSTakanori Watanabe conn_event =(ng_hci_le_connection_complete_ep *)(ev_pkt + 1); 10613ac41cceSTakanori Watanabe fprintf(stdout, "Handle: %d\n", le16toh(conn_event->handle)); 10623ac41cceSTakanori Watanabe if (verbose) { 10633ac41cceSTakanori Watanabe fprintf(stdout, 10643ac41cceSTakanori Watanabe "Status: %s\n", 10653ac41cceSTakanori Watanabe hci_status2str(conn_event->status)); 10663ac41cceSTakanori Watanabe fprintf(stdout, 10673ac41cceSTakanori Watanabe "Role: %s\n", 10683ac41cceSTakanori Watanabe hci_role2str(conn_event->role)); 10693ac41cceSTakanori Watanabe fprintf(stdout, 10703ac41cceSTakanori Watanabe "Address Type: %s\n", 10713ac41cceSTakanori Watanabe hci_addrtype2str(conn_event->address_type)); 10723ac41cceSTakanori Watanabe fprintf(stdout, 10733ac41cceSTakanori Watanabe "Address: %s\n", 10743ac41cceSTakanori Watanabe hci_bdaddr2str(&conn_event->address)); 10753ac41cceSTakanori Watanabe fprintf(stdout, 10763ac41cceSTakanori Watanabe "Interval: %.2fms\n", 10773ac41cceSTakanori Watanabe 6.25 * le16toh(conn_event->interval)); 10783ac41cceSTakanori Watanabe fprintf(stdout, 10793ac41cceSTakanori Watanabe "Latency: %d events\n", conn_event->latency); 10803ac41cceSTakanori Watanabe fprintf(stdout, 10813ac41cceSTakanori Watanabe "Supervision timeout: %dms\n", 10823ac41cceSTakanori Watanabe 10 * le16toh(conn_event->supervision_timeout)); 10833ac41cceSTakanori Watanabe fprintf(stdout, 1084f8143ed7STakanori Watanabe "Master clock accuracy: %s\n", 10853ac41cceSTakanori Watanabe hci_mc_accuracy2str( 10863ac41cceSTakanori Watanabe conn_event->master_clock_accuracy)); 10873ac41cceSTakanori Watanabe } 10883ac41cceSTakanori Watanabe } 10893ac41cceSTakanori Watanabe return; 10903ac41cceSTakanori Watanabe } 10913ac41cceSTakanori Watanabe 10922b2c6d69STakanori Watanabe static int 10932b2c6d69STakanori Watanabe le_read_channel_map(int s, int argc, char *argv[]) 10942b2c6d69STakanori Watanabe { 10952b2c6d69STakanori Watanabe ng_hci_le_read_channel_map_cp cp; 10962b2c6d69STakanori Watanabe ng_hci_le_read_channel_map_rp rp; 10972b2c6d69STakanori Watanabe int n; 10982b2c6d69STakanori Watanabe char buffer[2048]; 10992b2c6d69STakanori Watanabe 11002b2c6d69STakanori Watanabe /* parse command parameters */ 11012b2c6d69STakanori Watanabe switch (argc) { 11022b2c6d69STakanori Watanabe case 1: 11032b2c6d69STakanori Watanabe /* connection handle */ 11042b2c6d69STakanori Watanabe if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) 11052b2c6d69STakanori Watanabe return (USAGE); 11062b2c6d69STakanori Watanabe 11072b2c6d69STakanori Watanabe cp.connection_handle = (uint16_t) (n & 0x0fff); 11082b2c6d69STakanori Watanabe cp.connection_handle = htole16(cp.connection_handle); 11092b2c6d69STakanori Watanabe break; 11102b2c6d69STakanori Watanabe 11112b2c6d69STakanori Watanabe default: 11122b2c6d69STakanori Watanabe return (USAGE); 11132b2c6d69STakanori Watanabe } 11142b2c6d69STakanori Watanabe 11152b2c6d69STakanori Watanabe n = sizeof(rp); 11162b2c6d69STakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 11172b2c6d69STakanori Watanabe NG_HCI_OCF_LE_READ_CHANNEL_MAP), 11182b2c6d69STakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 11192b2c6d69STakanori Watanabe return (ERROR); 11202b2c6d69STakanori Watanabe 11212b2c6d69STakanori Watanabe if (rp.status != 0x00) { 11222b2c6d69STakanori Watanabe fprintf(stdout, 11232b2c6d69STakanori Watanabe "Read channel map failed. Status: %s [%#02x]\n", 11242b2c6d69STakanori Watanabe hci_status2str(rp.status), rp.status); 11252b2c6d69STakanori Watanabe return (FAILED); 11262b2c6d69STakanori Watanabe } 11272b2c6d69STakanori Watanabe 11282b2c6d69STakanori Watanabe fprintf(stdout, "Connection handle: %d\n", 11292b2c6d69STakanori Watanabe le16toh(rp.connection_handle)); 11302b2c6d69STakanori Watanabe fprintf(stdout, "Used channels:\n"); 11312b2c6d69STakanori Watanabe fprintf(stdout, "\n%s\n", hci_le_chanmap2str(rp.le_channel_map, 11322b2c6d69STakanori Watanabe buffer, sizeof(buffer))); 11332b2c6d69STakanori Watanabe 11342b2c6d69STakanori Watanabe return (OK); 11352b2c6d69STakanori Watanabe } /* le_read_channel_map */ 11362b2c6d69STakanori Watanabe 11372b2c6d69STakanori Watanabe static int 11382b2c6d69STakanori Watanabe le_read_remote_features(int s, int argc, char *argv[]) 11392b2c6d69STakanori Watanabe { 11402b2c6d69STakanori Watanabe ng_hci_le_read_remote_used_features_cp cp; 11412b2c6d69STakanori Watanabe ng_hci_status_rp rp; 11422b2c6d69STakanori Watanabe int n, bufsize; 11432b2c6d69STakanori Watanabe char b[512]; 11442b2c6d69STakanori Watanabe 11452b2c6d69STakanori Watanabe ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; 11462b2c6d69STakanori Watanabe 11472b2c6d69STakanori Watanabe /* parse command parameters */ 11482b2c6d69STakanori Watanabe switch (argc) { 11492b2c6d69STakanori Watanabe case 1: 11502b2c6d69STakanori Watanabe /* connection handle */ 11512b2c6d69STakanori Watanabe if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff) 11522b2c6d69STakanori Watanabe return (USAGE); 11532b2c6d69STakanori Watanabe 11542b2c6d69STakanori Watanabe cp.connection_handle = (uint16_t) (n & 0x0fff); 11552b2c6d69STakanori Watanabe cp.connection_handle = htole16(cp.connection_handle); 11562b2c6d69STakanori Watanabe break; 11572b2c6d69STakanori Watanabe 11582b2c6d69STakanori Watanabe default: 11592b2c6d69STakanori Watanabe return (USAGE); 11602b2c6d69STakanori Watanabe } 11612b2c6d69STakanori Watanabe 11622b2c6d69STakanori Watanabe n = sizeof(rp); 11632b2c6d69STakanori Watanabe if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 11642b2c6d69STakanori Watanabe NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES), 11652b2c6d69STakanori Watanabe (void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR) 11662b2c6d69STakanori Watanabe return (ERROR); 11672b2c6d69STakanori Watanabe 11682b2c6d69STakanori Watanabe if (rp.status != 0x00) { 11692b2c6d69STakanori Watanabe fprintf(stdout, 11702b2c6d69STakanori Watanabe "Read remote features failed. Status: %s [%#02x]\n", 11712b2c6d69STakanori Watanabe hci_status2str(rp.status), rp.status); 11722b2c6d69STakanori Watanabe return (FAILED); 11732b2c6d69STakanori Watanabe } 11742b2c6d69STakanori Watanabe 11752b2c6d69STakanori Watanabe /* wait for connection events */ 11762b2c6d69STakanori Watanabe bufsize = sizeof(b); 11772b2c6d69STakanori Watanabe if (hci_recv(s, b, &bufsize) == ERROR) { 11782b2c6d69STakanori Watanabe return (ERROR); 11792b2c6d69STakanori Watanabe } 11802b2c6d69STakanori Watanabe 11812b2c6d69STakanori Watanabe if (bufsize < sizeof(*e)) { 11822b2c6d69STakanori Watanabe errno = EIO; 11832b2c6d69STakanori Watanabe return (ERROR); 11842b2c6d69STakanori Watanabe } 11852b2c6d69STakanori Watanabe if (e->event == NG_HCI_EVENT_LE) { 11862b2c6d69STakanori Watanabe handle_le_remote_features_event(e); 11872b2c6d69STakanori Watanabe } 11882b2c6d69STakanori Watanabe 11892b2c6d69STakanori Watanabe return (OK); 11902b2c6d69STakanori Watanabe } /* le_read_remote_features */ 11912b2c6d69STakanori Watanabe 11922b2c6d69STakanori Watanabe static void handle_le_remote_features_event(ng_hci_event_pkt_t* e) 11932b2c6d69STakanori Watanabe { 11942b2c6d69STakanori Watanabe ng_hci_le_ep *ev_pkt; 11952b2c6d69STakanori Watanabe ng_hci_le_read_remote_features_ep *feat_event; 11962b2c6d69STakanori Watanabe char buffer[2048]; 11972b2c6d69STakanori Watanabe 11982b2c6d69STakanori Watanabe ev_pkt = (ng_hci_le_ep *)(e + 1); 11992b2c6d69STakanori Watanabe 12002b2c6d69STakanori Watanabe if (ev_pkt->subevent_code == NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL) { 12012b2c6d69STakanori Watanabe feat_event =(ng_hci_le_read_remote_features_ep *)(ev_pkt + 1); 12022b2c6d69STakanori Watanabe fprintf(stdout, "Handle: %d\n", 12032b2c6d69STakanori Watanabe le16toh(feat_event->connection_handle)); 12042b2c6d69STakanori Watanabe fprintf(stdout, 12052b2c6d69STakanori Watanabe "Status: %s\n", 12062b2c6d69STakanori Watanabe hci_status2str(feat_event->status)); 12072b2c6d69STakanori Watanabe fprintf(stdout, "Features:\n%s\n", 12082b2c6d69STakanori Watanabe hci_le_features2str(feat_event->features, 12092b2c6d69STakanori Watanabe buffer, sizeof(buffer))); 12102b2c6d69STakanori Watanabe } 12112b2c6d69STakanori Watanabe 12122b2c6d69STakanori Watanabe return; 12132b2c6d69STakanori Watanabe } /* handle_le_remote_features_event */ 12142b2c6d69STakanori Watanabe 1215*21056112STakanori Watanabe static int le_rand(int s, int argc, char *argv[]) 1216*21056112STakanori Watanabe { 1217*21056112STakanori Watanabe ng_hci_le_rand_rp rp; 1218*21056112STakanori Watanabe int n; 1219*21056112STakanori Watanabe 1220*21056112STakanori Watanabe n = sizeof(rp); 1221*21056112STakanori Watanabe 1222*21056112STakanori Watanabe if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, 1223*21056112STakanori Watanabe NG_HCI_OCF_LE_RAND), 1224*21056112STakanori Watanabe (void *)&rp, &n) == ERROR) 1225*21056112STakanori Watanabe return (ERROR); 1226*21056112STakanori Watanabe 1227*21056112STakanori Watanabe if (rp.status != 0x00) { 1228*21056112STakanori Watanabe fprintf(stdout, "Status: %s [%#02x]\n", 1229*21056112STakanori Watanabe hci_status2str(rp.status), rp.status); 1230*21056112STakanori Watanabe return (FAILED); 1231*21056112STakanori Watanabe } 1232*21056112STakanori Watanabe 1233*21056112STakanori Watanabe fprintf(stdout, 1234*21056112STakanori Watanabe "Random number : %08llx\n", 1235*21056112STakanori Watanabe (unsigned long long)le64toh(rp.random_number)); 1236*21056112STakanori Watanabe 1237*21056112STakanori Watanabe return (OK); 1238*21056112STakanori Watanabe } 1239*21056112STakanori Watanabe 12402b2c6d69STakanori Watanabe 12412b2c6d69STakanori Watanabe 1242bcff2d91STakanori Watanabe struct hci_command le_commands[] = { 1243bcff2d91STakanori Watanabe { 1244bcff2d91STakanori Watanabe "le_enable", 1245bcff2d91STakanori Watanabe "le_enable [enable|disable] \n" 1246bcff2d91STakanori Watanabe "Enable LE event ", 1247bcff2d91STakanori Watanabe &le_enable, 1248bcff2d91STakanori Watanabe }, 1249bcff2d91STakanori Watanabe { 1250bcff2d91STakanori Watanabe "le_read_local_supported_features", 1251bcff2d91STakanori Watanabe "le_read_local_supported_features\n" 1252bcff2d91STakanori Watanabe "read local supported features mask", 1253bcff2d91STakanori Watanabe &le_read_local_supported_features, 1254bcff2d91STakanori Watanabe }, 1255bcff2d91STakanori Watanabe { 125621eefd31SHans Petter Selasky "le_read_supported_states", 125721eefd31SHans Petter Selasky "le_read_supported_states\n" 1258bcff2d91STakanori Watanabe "read supported status" 1259bcff2d91STakanori Watanabe , 126021eefd31SHans Petter Selasky &le_read_supported_states, 1261bcff2d91STakanori Watanabe }, 1262bcff2d91STakanori Watanabe { 1263bcff2d91STakanori Watanabe "le_set_scan_response", 1264bcff2d91STakanori Watanabe "le_set_scan_response -n $name -f $flag -u $uuid16,$uuid16 \n" 1265bcff2d91STakanori Watanabe "set LE scan response data" 1266bcff2d91STakanori Watanabe , 1267bcff2d91STakanori Watanabe &le_set_scan_response, 1268bcff2d91STakanori Watanabe }, 1269bcff2d91STakanori Watanabe { 1270bcff2d91STakanori Watanabe "le_set_scan_enable", 1271bcff2d91STakanori Watanabe "le_set_scan_enable [enable|disable] \n" 1272bcff2d91STakanori Watanabe "enable or disable LE device scan", 1273bcff2d91STakanori Watanabe &le_set_scan_enable 1274bcff2d91STakanori Watanabe }, 1275bcff2d91STakanori Watanabe { 1276bcff2d91STakanori Watanabe "le_set_scan_param", 1277bcff2d91STakanori Watanabe "le_set_scan_param [active|passive] interval(ms) window(ms) [public|random] [all|whitelist] \n" 1278bcff2d91STakanori Watanabe "set LE device scan parameter", 1279bcff2d91STakanori Watanabe &le_set_scan_param 1280bcff2d91STakanori Watanabe }, 1281c3f60abcSHans Petter Selasky { 1282c3f60abcSHans Petter Selasky "le_set_advertising_enable", 1283c3f60abcSHans Petter Selasky "le_set_advertising_enable [enable|disable] \n" 1284c3f60abcSHans Petter Selasky "start or stop advertising", 1285c3f60abcSHans Petter Selasky &le_set_advertising_enable 1286c3f60abcSHans Petter Selasky }, 1287c3f60abcSHans Petter Selasky { 1288c3f60abcSHans Petter Selasky "le_read_advertising_channel_tx_power", 1289c3f60abcSHans Petter Selasky "le_read_advertising_channel_tx_power\n" 1290c3f60abcSHans Petter Selasky "read host advertising transmit poser level (dBm)", 1291c3f60abcSHans Petter Selasky &le_read_advertising_channel_tx_power 1292c3f60abcSHans Petter Selasky }, 1293c3f60abcSHans Petter Selasky { 1294c3f60abcSHans Petter Selasky "le_set_advertising_param", 1295c3f60abcSHans Petter Selasky "le_set_advertising_param [-m min_interval(ms)] [-M max_interval(ms)]\n" 1296c3f60abcSHans Petter Selasky "[-t advertising_type] [-o own_address_type] [-p peer_address_type]\n" 1297c3f60abcSHans Petter Selasky "[-c advertising_channel_map] [-f advertising_filter_policy]\n" 1298c3f60abcSHans Petter Selasky "[-a peer_address]\n" 1299c3f60abcSHans Petter Selasky "set LE device advertising parameters", 1300c3f60abcSHans Petter Selasky &le_set_advertising_param 1301c3f60abcSHans Petter Selasky }, 1302c3f60abcSHans Petter Selasky { 1303c3f60abcSHans Petter Selasky "le_set_advertising_data", 1304c3f60abcSHans Petter Selasky "le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 \n" 1305c3f60abcSHans Petter Selasky "set LE device advertising packed data", 1306c3f60abcSHans Petter Selasky &le_set_advertising_data 1307c3f60abcSHans Petter Selasky }, 13081f5d883dSTakanori Watanabe { 13091f5d883dSTakanori Watanabe "le_read_buffer_size", 13101f5d883dSTakanori Watanabe "le_read_buffer_size [-v 1|2]\n" 13111f5d883dSTakanori Watanabe "Read the maximum size of ACL and ISO data packets", 13121f5d883dSTakanori Watanabe &le_read_buffer_size 13131f5d883dSTakanori Watanabe }, 13149287f06dSTakanori Watanabe { 13159287f06dSTakanori Watanabe "le_scan", 13169287f06dSTakanori Watanabe "le_scan [-a] [-v] [-n number_of_scans]\n" 13179287f06dSTakanori Watanabe "Do an LE scan", 13189287f06dSTakanori Watanabe &le_scan 13199287f06dSTakanori Watanabe }, 132011fb4bdbSTakanori Watanabe { 132111fb4bdbSTakanori Watanabe "le_read_white_list_size", 132211fb4bdbSTakanori Watanabe "le_read_white_list_size\n" 132311fb4bdbSTakanori Watanabe "Read total number of white list entries that can be stored", 132411fb4bdbSTakanori Watanabe &le_read_white_list_size 132511fb4bdbSTakanori Watanabe }, 132611fb4bdbSTakanori Watanabe { 132711fb4bdbSTakanori Watanabe "le_clear_white_list", 132811fb4bdbSTakanori Watanabe "le_clear_white_list\n" 132911fb4bdbSTakanori Watanabe "Clear the white list in the controller", 133011fb4bdbSTakanori Watanabe &le_clear_white_list 133111fb4bdbSTakanori Watanabe }, 133211fb4bdbSTakanori Watanabe { 133311fb4bdbSTakanori Watanabe "le_add_device_to_white_list", 133411fb4bdbSTakanori Watanabe "le_add_device_to_white_list\n" 133511fb4bdbSTakanori Watanabe "[-t public|random] -a address\n" 133611fb4bdbSTakanori Watanabe "Add device to the white list", 133711fb4bdbSTakanori Watanabe &le_add_device_to_white_list 133811fb4bdbSTakanori Watanabe }, 133911fb4bdbSTakanori Watanabe { 134011fb4bdbSTakanori Watanabe "le_remove_device_from_white_list", 134111fb4bdbSTakanori Watanabe "le_remove_device_from_white_list\n" 134211fb4bdbSTakanori Watanabe "[-t public|random] -a address\n" 134311fb4bdbSTakanori Watanabe "Remove device from the white list", 134411fb4bdbSTakanori Watanabe &le_remove_device_from_white_list 134511fb4bdbSTakanori Watanabe }, 13463ac41cceSTakanori Watanabe { 13473ac41cceSTakanori Watanabe "le_connect", 13483ac41cceSTakanori Watanabe "le_connect -a address [-t public|random] [-v]\n" 13493ac41cceSTakanori Watanabe "Connect to an LE device", 13503ac41cceSTakanori Watanabe &le_connect 13513ac41cceSTakanori Watanabe }, 13522b2c6d69STakanori Watanabe { 13532b2c6d69STakanori Watanabe "le_read_channel_map", 13542b2c6d69STakanori Watanabe "le_read_channel_map <connection_handle>\n" 13552b2c6d69STakanori Watanabe "Read the channel map for a connection", 13562b2c6d69STakanori Watanabe &le_read_channel_map 13572b2c6d69STakanori Watanabe }, 13582b2c6d69STakanori Watanabe { 13592b2c6d69STakanori Watanabe "le_read_remote_features", 13602b2c6d69STakanori Watanabe "le_read_remote_features <connection_handle>\n" 13612b2c6d69STakanori Watanabe "Read supported features for the device\n" 13622b2c6d69STakanori Watanabe "identified by the connection handle", 13632b2c6d69STakanori Watanabe &le_read_remote_features 13642b2c6d69STakanori Watanabe }, 1365*21056112STakanori Watanabe { 1366*21056112STakanori Watanabe "le_rand", 1367*21056112STakanori Watanabe "le_rand\n" 1368*21056112STakanori Watanabe "Generate 64 bits of random data", 1369*21056112STakanori Watanabe &le_rand 1370*21056112STakanori Watanabe }, 1371bcff2d91STakanori Watanabe }; 1372