Path: blob/main/usr.sbin/bluetooth/hccontrol/le.c
105655 views
/*1* le.c2*3* Copyright (c) 2015 Takanori Watanabe <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*27* $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $28*/2930#include <sys/types.h>31#include <sys/ioctl.h>32#include <sys/sysctl.h>33#include <sys/select.h>34#include <assert.h>35#include <bitstring.h>36#include <err.h>37#include <errno.h>38#include <netgraph/ng_message.h>39#include <errno.h>40#include <stdbool.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>44#include <unistd.h>45#include <stdint.h>46#define L2CAP_SOCKET_CHECKED47#include <bluetooth.h>48#include "hccontrol.h"4950static int le_set_scan_param(int s, int argc, char *argv[]);51static int le_set_scan_enable(int s, int argc, char *argv[]);52static int parse_param(int argc, char *argv[], char *buf, int *len);53static int le_set_scan_response(int s, int argc, char *argv[]);54static int le_read_supported_states(int s, int argc, char *argv[]);55static int le_read_local_supported_features(int s, int argc ,char *argv[]);56static int set_le_event_mask(int s, uint64_t mask);57static int set_event_mask(int s, uint64_t mask);58static int le_enable(int s, int argc, char *argv[]);59static int le_set_advertising_enable(int s, int argc, char *argv[]);60static int le_set_advertising_param(int s, int argc, char *argv[]);61static int le_read_advertising_channel_tx_power(int s, int argc, char *argv[]);62static int le_scan(int s, int argc, char *argv[]);63static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose);64static int le_read_white_list_size(int s, int argc, char *argv[]);65static int le_clear_white_list(int s, int argc, char *argv[]);66static int le_add_device_to_white_list(int s, int argc, char *argv[]);67static int le_remove_device_from_white_list(int s, int argc, char *argv[]);68static int le_connect(int s, int argc, char *argv[]);69static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose);70static int le_read_channel_map(int s, int argc, char *argv[]);71static void handle_le_remote_features_event(ng_hci_event_pkt_t* e);72static int le_rand(int s, int argc, char *argv[]);7374static int75le_set_scan_param(int s, int argc, char *argv[])76{77int type;78int interval;79int window;80int adrtype;81int policy;82int n;8384ng_hci_le_set_scan_parameters_cp cp;85ng_hci_le_set_scan_parameters_rp rp;8687if (argc != 5)88return (USAGE);8990if (strcmp(argv[0], "active") == 0)91type = 1;92else if (strcmp(argv[0], "passive") == 0)93type = 0;94else95return (USAGE);9697interval = (int)(atof(argv[1])/0.625);98interval = (interval < 4)? 4: interval;99window = (int)(atof(argv[2])/0.625);100window = (window < 4) ? 4 : interval;101102if (strcmp(argv[3], "public") == 0)103adrtype = 0;104else if (strcmp(argv[3], "random") == 0)105adrtype = 1;106else107return (USAGE);108109if (strcmp(argv[4], "all") == 0)110policy = 0;111else if (strcmp(argv[4], "whitelist") == 0)112policy = 1;113else114return (USAGE);115116cp.le_scan_type = type;117cp.le_scan_interval = interval;118cp.own_address_type = adrtype;119cp.le_scan_window = window;120cp.scanning_filter_policy = policy;121n = sizeof(rp);122123if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,124NG_HCI_OCF_LE_SET_SCAN_PARAMETERS),125(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)126return (ERROR);127128if (rp.status != 0x00) {129fprintf(stdout, "Status: %s [%#02x]\n",130hci_status2str(rp.status), rp.status);131return (FAILED);132}133134return (OK);135}136137static int138le_set_scan_enable(int s, int argc, char *argv[])139{140ng_hci_le_set_scan_enable_cp cp;141ng_hci_le_set_scan_enable_rp rp;142int n, enable = 0;143144if (argc != 1)145return (USAGE);146147if (strcmp(argv[0], "enable") == 0)148enable = 1;149else if (strcmp(argv[0], "disable") != 0)150return (USAGE);151152n = sizeof(rp);153cp.le_scan_enable = enable;154cp.filter_duplicates = 0;155if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,156NG_HCI_OCF_LE_SET_SCAN_ENABLE),157(void *)&cp, sizeof(cp),158(void *)&rp, &n) == ERROR)159return (ERROR);160161if (rp.status != 0x00) {162fprintf(stdout, "Status: %s [%#02x]\n",163hci_status2str(rp.status), rp.status);164return (FAILED);165}166167fprintf(stdout, "LE Scan: %s\n",168enable? "Enabled" : "Disabled");169170return (OK);171}172173static int174parse_param(int argc, char *argv[], char *buf, int *len)175{176char *buflast = buf + (*len);177char *curbuf = buf;178char *token,*lenpos;179int ch;180int datalen;181uint16_t value;182optreset = 1;183optind = 0;184while ((ch = getopt(argc, argv , "n:f:u:")) != -1) {185switch(ch){186case 'n':187datalen = strlen(optarg);188if ((curbuf + datalen + 2) >= buflast)189goto done;190curbuf[0] = datalen + 1;191curbuf[1] = 8;192curbuf += 2;193memcpy(curbuf, optarg, datalen);194curbuf += datalen;195break;196case 'f':197if (curbuf+3 > buflast)198goto done;199curbuf[0] = 2;200curbuf[1] = 1;201curbuf[2] = (uint8_t)strtol(optarg, NULL, 16);202curbuf += 3;203break;204case 'u':205if ((buf+2) >= buflast)206goto done;207lenpos = curbuf;208curbuf[1] = 2;209*lenpos = 1;210curbuf += 2;211while ((token = strsep(&optarg, ",")) != NULL) {212value = strtol(token, NULL, 16);213if ((curbuf+2) >= buflast)214break;215curbuf[0] = value &0xff;216curbuf[1] = (value>>8)&0xff;217curbuf += 2;218*lenpos += 2;219}220221}222}223done:224*len = curbuf - buf;225226return (OK);227}228229static int230le_set_scan_response(int s, int argc, char *argv[])231{232ng_hci_le_set_scan_response_data_cp cp;233ng_hci_le_set_scan_response_data_rp rp;234int n;235int len;236char buf[NG_HCI_ADVERTISING_DATA_SIZE];237238len = sizeof(buf);239parse_param(argc, argv, buf, &len);240memset(cp.scan_response_data, 0, sizeof(cp.scan_response_data));241cp.scan_response_data_length = len;242memcpy(cp.scan_response_data, buf, len);243n = sizeof(rp);244if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,245NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA),246(void *)&cp, sizeof(cp),247(void *)&rp, &n) == ERROR)248return (ERROR);249250if (rp.status != 0x00) {251fprintf(stdout, "Status: %s [%#02x]\n",252hci_status2str(rp.status), rp.status);253return (FAILED);254}255256return (OK);257}258259static int260le_read_local_supported_features(int s, int argc ,char *argv[])261{262ng_hci_le_read_local_supported_features_rp rp;263int n = sizeof(rp);264265union {266uint64_t raw;267uint8_t octets[8];268} le_features;269270char buffer[2048];271272if (hci_simple_request(s,273NG_HCI_OPCODE(NG_HCI_OGF_LE,274NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES),275(void *)&rp, &n) == ERROR)276return (ERROR);277278if (rp.status != 0x00) {279fprintf(stdout, "Status: %s [%#02x]\n",280hci_status2str(rp.status), rp.status);281return (FAILED);282}283284le_features.raw = rp.le_features;285286fprintf(stdout, "LE Features: ");287for(int i = 0; i < 8; i++)288fprintf(stdout, " %#02x", le_features.octets[i]);289fprintf(stdout, "\n%s\n", hci_le_features2str(le_features.octets,290buffer, sizeof(buffer)));291fprintf(stdout, "\n");292293return (OK);294}295296static int297le_read_supported_states(int s, int argc, char *argv[])298{299ng_hci_le_read_supported_states_rp rp;300int n = sizeof(rp);301302if (hci_simple_request(s, NG_HCI_OPCODE(303NG_HCI_OGF_LE,304NG_HCI_OCF_LE_READ_SUPPORTED_STATES),305(void *)&rp, &n) == ERROR)306return (ERROR);307308if (rp.status != 0x00) {309fprintf(stdout, "Status: %s [%#02x]\n",310hci_status2str(rp.status), rp.status);311return (FAILED);312}313314fprintf(stdout, "LE States: %jx\n", rp.le_states);315316return (OK);317}318319static int320set_le_event_mask(int s, uint64_t mask)321{322ng_hci_le_set_event_mask_cp semc;323ng_hci_le_set_event_mask_rp rp;324int i, n;325326n = sizeof(rp);327328for (i=0; i < NG_HCI_LE_EVENT_MASK_SIZE; i++) {329semc.event_mask[i] = mask&0xff;330mask >>= 8;331}332if(hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,333NG_HCI_OCF_LE_SET_EVENT_MASK),334(void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR)335return (ERROR);336337if (rp.status != 0x00) {338fprintf(stdout, "Status: %s [%#02x]\n",339hci_status2str(rp.status), rp.status);340return (FAILED);341}342343return (OK);344}345346static int347set_event_mask(int s, uint64_t mask)348{349ng_hci_set_event_mask_cp semc;350ng_hci_set_event_mask_rp rp;351int i, n;352353n = sizeof(rp);354355for (i=0; i < NG_HCI_EVENT_MASK_SIZE; i++) {356semc.event_mask[i] = mask&0xff;357mask >>= 8;358}359if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,360NG_HCI_OCF_SET_EVENT_MASK),361(void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR)362return (ERROR);363364if (rp.status != 0x00) {365fprintf(stdout, "Status: %s [%#02x]\n",366hci_status2str(rp.status), rp.status);367return (FAILED);368}369370return (OK);371}372373static374int le_enable(int s, int argc, char *argv[])375{376int result;377378if (argc != 1)379return (USAGE);380381if (strcasecmp(argv[0], "enable") == 0) {382result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT |383NG_HCI_EVENT_MASK_LE);384if (result != OK)385return result;386result = set_le_event_mask(s, NG_HCI_LE_EVENT_MASK_ALL);387if (result == OK) {388fprintf(stdout, "LE enabled\n");389return (OK);390} else391return result;392} else if (strcasecmp(argv[0], "disable") == 0) {393result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT);394if (result == OK) {395fprintf(stdout, "LE disabled\n");396return (OK);397} else398return result;399} else400return (USAGE);401}402403static int404le_set_advertising_enable(int s, int argc, char *argv[])405{406ng_hci_le_set_advertise_enable_cp cp;407ng_hci_le_set_advertise_enable_rp rp;408int n, enable = 0;409410if (argc != 1)411return USAGE;412413if (strcmp(argv[0], "enable") == 0)414enable = 1;415else if (strcmp(argv[0], "disable") != 0)416return USAGE;417418n = sizeof(rp);419cp.advertising_enable = enable;420if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,421NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE),422(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)423return (ERROR);424425if (rp.status != 0x00) {426fprintf(stdout, "Status: %s [%#02x]\n",427hci_status2str(rp.status), rp.status);428return (FAILED);429}430fprintf(stdout, "LE Advertising %s\n", (enable ? "enabled" : "disabled"));431432return (OK);433}434435static int436le_set_advertising_param(int s, int argc, char *argv[])437{438ng_hci_le_set_advertising_parameters_cp cp;439ng_hci_le_set_advertising_parameters_rp rp;440441int n, ch;442443cp.advertising_interval_min = 0x800;444cp.advertising_interval_max = 0x800;445cp.advertising_type = 0;446cp.own_address_type = 0;447cp.direct_address_type = 0;448449cp.advertising_channel_map = 7;450cp.advertising_filter_policy = 0;451452optreset = 1;453optind = 0;454while ((ch = getopt(argc, argv , "m:M:t:o:p:a:c:f:")) != -1) {455switch(ch) {456case 'm':457cp.advertising_interval_min =458(uint16_t)(strtod(optarg, NULL)/0.625);459break;460case 'M':461cp.advertising_interval_max =462(uint16_t)(strtod(optarg, NULL)/0.625);463break;464case 't':465cp.advertising_type =466(uint8_t)strtod(optarg, NULL);467break;468case 'o':469cp.own_address_type =470(uint8_t)strtod(optarg, NULL);471break;472case 'p':473cp.direct_address_type =474(uint8_t)strtod(optarg, NULL);475break;476case 'a':477if (!bt_aton(optarg, &cp.direct_address)) {478struct hostent *he = NULL;479480if ((he = bt_gethostbyname(optarg)) == NULL)481return (USAGE);482483memcpy(&cp.direct_address, he->h_addr, sizeof(cp.direct_address));484}485break;486case 'c':487cp.advertising_channel_map =488(uint8_t)strtod(optarg, NULL);489break;490case 'f':491cp.advertising_filter_policy =492(uint8_t)strtod(optarg, NULL);493break;494}495}496497n = sizeof(rp);498if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,499NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS),500(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)501return (ERROR);502503if (rp.status != 0x00) {504fprintf(stdout, "Status: %s [%#02x]\n",505hci_status2str(rp.status), rp.status);506return (FAILED);507}508509return (OK);510}511512static int513le_read_advertising_channel_tx_power(int s, int argc, char *argv[])514{515ng_hci_le_read_advertising_channel_tx_power_rp rp;516int n;517518n = sizeof(rp);519520if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,521NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER),522(void *)&rp, &n) == ERROR)523return (ERROR);524525if (rp.status != 0x00) {526fprintf(stdout, "Status: %s [%#02x]\n",527hci_status2str(rp.status), rp.status);528return (FAILED);529}530531fprintf(stdout, "Advertising transmit power level: %d dBm\n",532(int8_t)rp.transmit_power_level);533534return (OK);535}536537static int538le_set_advertising_data(int s, int argc, char *argv[])539{540ng_hci_le_set_advertising_data_cp cp;541ng_hci_le_set_advertising_data_rp rp;542int n, len;543544n = sizeof(rp);545546char buf[NG_HCI_ADVERTISING_DATA_SIZE];547548len = sizeof(buf);549parse_param(argc, argv, buf, &len);550memset(cp.advertising_data, 0, sizeof(cp.advertising_data));551cp.advertising_data_length = len;552memcpy(cp.advertising_data, buf, len);553554if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,555NG_HCI_OCF_LE_SET_ADVERTISING_DATA),556(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)557return (ERROR);558559if (rp.status != 0x00) {560fprintf(stdout, "Status: %s [%#02x]\n",561hci_status2str(rp.status), rp.status);562return (FAILED);563}564565return (OK);566}567static int568le_read_buffer_size(int s, int argc, char *argv[])569{570union {571ng_hci_le_read_buffer_size_rp v1;572ng_hci_le_read_buffer_size_rp_v2 v2;573} rp;574575int n, ch;576uint8_t v;577uint16_t cmd;578579optreset = 1;580optind = 0;581582/* Default to version 1*/583v = 1;584cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE;585586while ((ch = getopt(argc, argv , "v:")) != -1) {587switch(ch) {588case 'v':589v = (uint8_t)strtol(optarg, NULL, 16);590if (v == 2)591cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE_V2;592else if (v > 2)593return (USAGE);594break;595default:596v = 1;597}598}599600n = sizeof(rp);601if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, cmd),602(void *)&rp, &n) == ERROR)603return (ERROR);604605if (rp.v1.status != 0x00) {606fprintf(stdout, "Status: %s [%#02x]\n",607hci_status2str(rp.v1.status), rp.v1.status);608return (FAILED);609}610611fprintf(stdout, "ACL data packet length: %d\n",612rp.v1.hc_le_data_packet_length);613fprintf(stdout, "Number of ACL data packets: %d\n",614rp.v1.hc_total_num_le_data_packets);615616if (v == 2) {617fprintf(stdout, "ISO data packet length: %d\n",618rp.v2.hc_iso_data_packet_length);619fprintf(stdout, "Number of ISO data packets: %d\n",620rp.v2.hc_total_num_iso_data_packets);621}622623return (OK);624}625626static int627le_scan(int s, int argc, char *argv[])628{629int n, bufsize, scancount, numscans;630bool verbose;631uint8_t active = 0;632char ch;633634char b[512];635ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;636637ng_hci_le_set_scan_parameters_cp scan_param_cp;638ng_hci_le_set_scan_parameters_rp scan_param_rp;639640ng_hci_le_set_scan_enable_cp scan_enable_cp;641ng_hci_le_set_scan_enable_rp scan_enable_rp;642643optreset = 1;644optind = 0;645verbose = false;646numscans = 1;647648while ((ch = getopt(argc, argv , "an:v")) != -1) {649switch(ch) {650case 'a':651active = 1;652break;653case 'n':654numscans = (uint8_t)strtol(optarg, NULL, 10);655break;656case 'v':657verbose = true;658break;659}660}661662scan_param_cp.le_scan_type = active;663scan_param_cp.le_scan_interval = (uint16_t)(100/0.625);664scan_param_cp.le_scan_window = (uint16_t)(50/0.625);665/* Address type public */666scan_param_cp.own_address_type = 0;667/* 'All' filter policy */668scan_param_cp.scanning_filter_policy = 0;669n = sizeof(scan_param_rp);670671if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,672NG_HCI_OCF_LE_SET_SCAN_PARAMETERS),673(void *)&scan_param_cp, sizeof(scan_param_cp),674(void *)&scan_param_rp, &n) == ERROR)675return (ERROR);676677if (scan_param_rp.status != 0x00) {678fprintf(stdout, "LE_Set_Scan_Parameters failed. Status: %s [%#02x]\n",679hci_status2str(scan_param_rp.status),680scan_param_rp.status);681return (FAILED);682}683684/* Enable scanning */685n = sizeof(scan_enable_rp);686scan_enable_cp.le_scan_enable = 1;687scan_enable_cp.filter_duplicates = 1;688if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,689NG_HCI_OCF_LE_SET_SCAN_ENABLE),690(void *)&scan_enable_cp, sizeof(scan_enable_cp),691(void *)&scan_enable_rp, &n) == ERROR)692return (ERROR);693694if (scan_enable_rp.status != 0x00) {695fprintf(stdout, "LE_Scan_Enable enable failed. Status: %s [%#02x]\n",696hci_status2str(scan_enable_rp.status),697scan_enable_rp.status);698return (FAILED);699}700701scancount = 0;702while (scancount < numscans) {703/* wait for scan events */704bufsize = sizeof(b);705if (hci_recv(s, b, &bufsize) == ERROR) {706return (ERROR);707}708709if (bufsize < sizeof(*e)) {710errno = EIO;711return (ERROR);712}713scancount++;714if (e->event == NG_HCI_EVENT_LE) {715fprintf(stdout, "Scan %d\n", scancount);716handle_le_event(e, verbose);717}718}719720fprintf(stdout, "Scan complete\n");721722/* Disable scanning */723n = sizeof(scan_enable_rp);724scan_enable_cp.le_scan_enable = 0;725if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,726NG_HCI_OCF_LE_SET_SCAN_ENABLE),727(void *)&scan_enable_cp, sizeof(scan_enable_cp),728(void *)&scan_enable_rp, &n) == ERROR)729return (ERROR);730731if (scan_enable_rp.status != 0x00) {732fprintf(stdout, "LE_Scan_Enable disable failed. Status: %s [%#02x]\n",733hci_status2str(scan_enable_rp.status),734scan_enable_rp.status);735return (FAILED);736}737738return (OK);739}740741static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose)742{743int rc;744ng_hci_le_ep *leer =745(ng_hci_le_ep *)(e + 1);746ng_hci_le_advertising_report_ep *advrep =747(ng_hci_le_advertising_report_ep *)(leer + 1);748ng_hci_le_advreport *reports =749(ng_hci_le_advreport *)(advrep + 1);750751if (leer->subevent_code == NG_HCI_LEEV_ADVREP) {752fprintf(stdout, "Scan result, num_reports: %d\n",753advrep->num_reports);754for(rc = 0; rc < advrep->num_reports; rc++) {755uint8_t length = (uint8_t)reports[rc].length_data;756fprintf(stdout, "\tBD_ADDR %s \n",757hci_bdaddr2str(&reports[rc].bdaddr));758fprintf(stdout, "\tAddress type: %s\n",759hci_addrtype2str(reports[rc].addr_type));760if (length > 0 && verbose) {761dump_adv_data(length, reports[rc].data);762print_adv_data(length, reports[rc].data);763fprintf(stdout,764"\tRSSI: %d dBm\n",765(int8_t)reports[rc].data[length]);766fprintf(stdout, "\n");767}768}769}770}771772static int773le_read_white_list_size(int s, int argc, char *argv[])774{775ng_hci_le_read_white_list_size_rp rp;776int n;777778n = sizeof(rp);779780if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,781NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE),782(void *)&rp, &n) == ERROR)783return (ERROR);784785if (rp.status != 0x00) {786fprintf(stdout, "Status: %s [%#02x]\n",787hci_status2str(rp.status), rp.status);788return (FAILED);789}790791fprintf(stdout, "White list size: %d\n",792(uint8_t)rp.white_list_size);793794return (OK);795}796797static int798le_clear_white_list(int s, int argc, char *argv[])799{800ng_hci_le_clear_white_list_rp rp;801int n;802803n = sizeof(rp);804805if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,806NG_HCI_OCF_LE_CLEAR_WHITE_LIST),807(void *)&rp, &n) == ERROR)808return (ERROR);809810if (rp.status != 0x00) {811fprintf(stdout, "Status: %s [%#02x]\n",812hci_status2str(rp.status), rp.status);813return (FAILED);814}815816fprintf(stdout, "White list cleared\n");817818return (OK);819}820821static int822le_add_device_to_white_list(int s, int argc, char *argv[])823{824ng_hci_le_add_device_to_white_list_cp cp;825ng_hci_le_add_device_to_white_list_rp rp;826int n;827char ch;828optreset = 1;829optind = 0;830bool addr_set = false;831832n = sizeof(rp);833834cp.address_type = 0x00;835836while ((ch = getopt(argc, argv , "t:a:")) != -1) {837switch(ch) {838case 't':839if (strcmp(optarg, "public") == 0)840cp.address_type = 0x00;841else if (strcmp(optarg, "random") == 0)842cp.address_type = 0x01;843else844return (USAGE);845break;846case 'a':847addr_set = true;848if (!bt_aton(optarg, &cp.address)) {849struct hostent *he = NULL;850851if ((he = bt_gethostbyname(optarg)) == NULL)852return (USAGE);853854memcpy(&cp.address, he->h_addr,855sizeof(cp.address));856}857break;858}859}860861if (addr_set == false)862return (USAGE);863864if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,865NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST),866(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)867return (ERROR);868869if (rp.status != 0x00) {870fprintf(stdout, "Status: %s [%#02x]\n",871hci_status2str(rp.status), rp.status);872return (FAILED);873}874875fprintf(stdout, "Address added to white list\n");876877return (OK);878}879880static int881le_remove_device_from_white_list(int s, int argc, char *argv[])882{883ng_hci_le_remove_device_from_white_list_cp cp;884ng_hci_le_remove_device_from_white_list_rp rp;885int n;886char ch;887optreset = 1;888optind = 0;889bool addr_set = false;890891n = sizeof(rp);892893cp.address_type = 0x00;894895while ((ch = getopt(argc, argv , "t:a:")) != -1) {896switch(ch) {897case 't':898if (strcmp(optarg, "public") == 0)899cp.address_type = 0x00;900else if (strcmp(optarg, "random") == 0)901cp.address_type = 0x01;902else903return (USAGE);904break;905case 'a':906addr_set = true;907if (!bt_aton(optarg, &cp.address)) {908struct hostent *he = NULL;909910if ((he = bt_gethostbyname(optarg)) == NULL)911return (USAGE);912913memcpy(&cp.address, he->h_addr,914sizeof(cp.address));915}916break;917}918}919920if (addr_set == false)921return (USAGE);922923if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,924NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST),925(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)926return (ERROR);927928if (rp.status != 0x00) {929fprintf(stdout, "Status: %s [%#02x]\n",930hci_status2str(rp.status), rp.status);931return (FAILED);932}933934fprintf(stdout, "Address removed from white list\n");935936return (OK);937}938939static int940le_connect(int s, int argc, char *argv[])941{942ng_hci_le_create_connection_cp cp;943ng_hci_status_rp rp;944char b[512];945ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;946947int n, scancount, bufsize;948char ch;949bool addr_set = false;950bool verbose = false;951952optreset = 1;953optind = 0;954955/* minimal scan interval (2.5ms) */956cp.scan_interval = htole16(4);957cp.scan_window = htole16(4);958959/* Don't use the whitelist */960cp.filter_policy = 0x00;961962/* Default to public peer address */963cp.peer_addr_type = 0x00;964965/* Own address type public */966cp.own_address_type = 0x00;967968/* 18.75ms min connection interval */969cp.conn_interval_min = htole16(0x000F);970/* 18.75ms max connection interval */971cp.conn_interval_max = htole16(0x000F);972973/* 0 events connection latency */974cp.conn_latency = htole16(0x0000);975976/* 32s supervision timeout */977cp.supervision_timeout = htole16(0x0C80);978979/* Min CE Length 0.625 ms */980cp.min_ce_length = htole16(1);981/* Max CE Length 0.625 ms */982cp.max_ce_length = htole16(1);983984while ((ch = getopt(argc, argv , "a:t:v")) != -1) {985switch(ch) {986case 't':987if (strcmp(optarg, "public") == 0)988cp.peer_addr_type = 0x00;989else if (strcmp(optarg, "random") == 0)990cp.peer_addr_type = 0x01;991else992return (USAGE);993break;994case 'a':995addr_set = true;996if (!bt_aton(optarg, &cp.peer_addr)) {997struct hostent *he = NULL;998999if ((he = bt_gethostbyname(optarg)) == NULL)1000return (USAGE);10011002memcpy(&cp.peer_addr, he->h_addr,1003sizeof(cp.peer_addr));1004}1005break;1006case 'v':1007verbose = true;1008break;1009}1010}10111012if (addr_set == false)1013return (USAGE);10141015n = sizeof(rp);1016if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,1017NG_HCI_OCF_LE_CREATE_CONNECTION),1018(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)1019return (ERROR);10201021if (rp.status != 0x00) {1022fprintf(stdout,1023"Create connection failed. Status: %s [%#02x]\n",1024hci_status2str(rp.status), rp.status);1025return (FAILED);1026}10271028scancount = 0;1029while (scancount < 3) {1030/* wait for connection events */1031bufsize = sizeof(b);1032if (hci_recv(s, b, &bufsize) == ERROR) {1033return (ERROR);1034}10351036if (bufsize < sizeof(*e)) {1037errno = EIO;1038return (ERROR);1039}1040scancount++;1041if (e->event == NG_HCI_EVENT_LE) {1042handle_le_connection_event(e, verbose);1043break;1044}1045}10461047return (OK);1048}10491050static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose)1051{1052ng_hci_le_ep *ev_pkt;1053ng_hci_le_connection_complete_ep *conn_event;10541055ev_pkt = (ng_hci_le_ep *)(e + 1);10561057if (ev_pkt->subevent_code == NG_HCI_LEEV_CON_COMPL) {1058conn_event =(ng_hci_le_connection_complete_ep *)(ev_pkt + 1);1059fprintf(stdout, "Handle: %d\n", le16toh(conn_event->handle));1060if (verbose) {1061fprintf(stdout,1062"Status: %s\n",1063hci_status2str(conn_event->status));1064fprintf(stdout,1065"Role: %s\n",1066hci_role2str(conn_event->role));1067fprintf(stdout,1068"Address Type: %s\n",1069hci_addrtype2str(conn_event->address_type));1070fprintf(stdout,1071"Address: %s\n",1072hci_bdaddr2str(&conn_event->address));1073fprintf(stdout,1074"Interval: %.2fms\n",10756.25 * le16toh(conn_event->interval));1076fprintf(stdout,1077"Latency: %d events\n", conn_event->latency);1078fprintf(stdout,1079"Supervision timeout: %dms\n",108010 * le16toh(conn_event->supervision_timeout));1081fprintf(stdout,1082"Master clock accuracy: %s\n",1083hci_mc_accuracy2str(1084conn_event->master_clock_accuracy));1085}1086}1087}10881089static int1090le_read_channel_map(int s, int argc, char *argv[])1091{1092ng_hci_le_read_channel_map_cp cp;1093ng_hci_le_read_channel_map_rp rp;1094int n;1095char buffer[2048];10961097/* parse command parameters */1098switch (argc) {1099case 1:1100/* connection handle */1101if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)1102return (USAGE);11031104cp.connection_handle = (uint16_t) (n & 0x0fff);1105cp.connection_handle = htole16(cp.connection_handle);1106break;11071108default:1109return (USAGE);1110}11111112n = sizeof(rp);1113if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,1114NG_HCI_OCF_LE_READ_CHANNEL_MAP),1115(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)1116return (ERROR);11171118if (rp.status != 0x00) {1119fprintf(stdout,1120"Read channel map failed. Status: %s [%#02x]\n",1121hci_status2str(rp.status), rp.status);1122return (FAILED);1123}11241125fprintf(stdout, "Connection handle: %d\n",1126le16toh(rp.connection_handle));1127fprintf(stdout, "Used channels:\n");1128fprintf(stdout, "\n%s\n", hci_le_chanmap2str(rp.le_channel_map,1129buffer, sizeof(buffer)));11301131return (OK);1132} /* le_read_channel_map */11331134static int1135le_read_remote_features(int s, int argc, char *argv[])1136{1137ng_hci_le_read_remote_used_features_cp cp;1138ng_hci_status_rp rp;1139int n, bufsize;1140char b[512];11411142ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;11431144/* parse command parameters */1145switch (argc) {1146case 1:1147/* connection handle */1148if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)1149return (USAGE);11501151cp.connection_handle = (uint16_t) (n & 0x0fff);1152cp.connection_handle = htole16(cp.connection_handle);1153break;11541155default:1156return (USAGE);1157}11581159n = sizeof(rp);1160if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,1161NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES),1162(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)1163return (ERROR);11641165if (rp.status != 0x00) {1166fprintf(stdout,1167"Read remote features failed. Status: %s [%#02x]\n",1168hci_status2str(rp.status), rp.status);1169return (FAILED);1170}11711172/* wait for connection events */1173bufsize = sizeof(b);1174if (hci_recv(s, b, &bufsize) == ERROR) {1175return (ERROR);1176}11771178if (bufsize < sizeof(*e)) {1179errno = EIO;1180return (ERROR);1181}1182if (e->event == NG_HCI_EVENT_LE) {1183handle_le_remote_features_event(e);1184}11851186return (OK);1187} /* le_read_remote_features */11881189static void handle_le_remote_features_event(ng_hci_event_pkt_t* e)1190{1191ng_hci_le_ep *ev_pkt;1192ng_hci_le_read_remote_features_ep *feat_event;1193char buffer[2048];11941195ev_pkt = (ng_hci_le_ep *)(e + 1);11961197if (ev_pkt->subevent_code == NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL) {1198feat_event =(ng_hci_le_read_remote_features_ep *)(ev_pkt + 1);1199fprintf(stdout, "Handle: %d\n",1200le16toh(feat_event->connection_handle));1201fprintf(stdout,1202"Status: %s\n",1203hci_status2str(feat_event->status));1204fprintf(stdout, "Features:\n%s\n",1205hci_le_features2str(feat_event->features,1206buffer, sizeof(buffer)));1207}1208} /* handle_le_remote_features_event */12091210static int le_rand(int s, int argc, char *argv[])1211{1212ng_hci_le_rand_rp rp;1213int n;12141215n = sizeof(rp);12161217if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,1218NG_HCI_OCF_LE_RAND),1219(void *)&rp, &n) == ERROR)1220return (ERROR);12211222if (rp.status != 0x00) {1223fprintf(stdout, "Status: %s [%#02x]\n",1224hci_status2str(rp.status), rp.status);1225return (FAILED);1226}12271228fprintf(stdout,1229"Random number : %08llx\n",1230(unsigned long long)le64toh(rp.random_number));12311232return (OK);1233}1234123512361237struct hci_command le_commands[] = {1238{1239"le_enable",1240"le_enable [enable|disable] \n"1241"Enable LE event ",1242&le_enable,1243},1244{1245"le_read_local_supported_features",1246"le_read_local_supported_features\n"1247"read local supported features mask",1248&le_read_local_supported_features,1249},1250{1251"le_read_supported_states",1252"le_read_supported_states\n"1253"read supported status"1254,1255&le_read_supported_states,1256},1257{1258"le_set_scan_response",1259"le_set_scan_response -n $name -f $flag -u $uuid16,$uuid16 \n"1260"set LE scan response data"1261,1262&le_set_scan_response,1263},1264{1265"le_set_scan_enable",1266"le_set_scan_enable [enable|disable] \n"1267"enable or disable LE device scan",1268&le_set_scan_enable1269},1270{1271"le_set_scan_param",1272"le_set_scan_param [active|passive] interval(ms) window(ms) [public|random] [all|whitelist] \n"1273"set LE device scan parameter",1274&le_set_scan_param1275},1276{1277"le_set_advertising_enable",1278"le_set_advertising_enable [enable|disable] \n"1279"start or stop advertising",1280&le_set_advertising_enable1281},1282{1283"le_read_advertising_channel_tx_power",1284"le_read_advertising_channel_tx_power\n"1285"read host advertising transmit poser level (dBm)",1286&le_read_advertising_channel_tx_power1287},1288{1289"le_set_advertising_param",1290"le_set_advertising_param [-m min_interval(ms)] [-M max_interval(ms)]\n"1291"[-t advertising_type] [-o own_address_type] [-p peer_address_type]\n"1292"[-c advertising_channel_map] [-f advertising_filter_policy]\n"1293"[-a peer_address]\n"1294"set LE device advertising parameters",1295&le_set_advertising_param1296},1297{1298"le_set_advertising_data",1299"le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 \n"1300"set LE device advertising packed data",1301&le_set_advertising_data1302},1303{1304"le_read_buffer_size",1305"le_read_buffer_size [-v 1|2]\n"1306"Read the maximum size of ACL and ISO data packets",1307&le_read_buffer_size1308},1309{1310"le_scan",1311"le_scan [-a] [-v] [-n number_of_scans]\n"1312"Do an LE scan",1313&le_scan1314},1315{1316"le_read_white_list_size",1317"le_read_white_list_size\n"1318"Read total number of white list entries that can be stored",1319&le_read_white_list_size1320},1321{1322"le_clear_white_list",1323"le_clear_white_list\n"1324"Clear the white list in the controller",1325&le_clear_white_list1326},1327{1328"le_add_device_to_white_list",1329"le_add_device_to_white_list\n"1330"[-t public|random] -a address\n"1331"Add device to the white list",1332&le_add_device_to_white_list1333},1334{1335"le_remove_device_from_white_list",1336"le_remove_device_from_white_list\n"1337"[-t public|random] -a address\n"1338"Remove device from the white list",1339&le_remove_device_from_white_list1340},1341{1342"le_connect",1343"le_connect -a address [-t public|random] [-v]\n"1344"Connect to an LE device",1345&le_connect1346},1347{1348"le_read_channel_map",1349"le_read_channel_map <connection_handle>\n"1350"Read the channel map for a connection",1351&le_read_channel_map1352},1353{1354"le_read_remote_features",1355"le_read_remote_features <connection_handle>\n"1356"Read supported features for the device\n"1357"identified by the connection handle",1358&le_read_remote_features1359},1360{1361"le_rand",1362"le_rand\n"1363"Generate 64 bits of random data",1364&le_rand1365},1366{1367NULL,1368}1369};137013711372