/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include /* BLE */ #include "nimble/ble.h" #include "nimble/ble_hci_trans.h" #include "nimble/hci_common.h" #include "host/ble_hs.h" #include "nimble/nimble_npl.h" #include "nimble/npl_shell.h" /* XXX: An app should not include private headers from a library. The bletest * app uses some of nimble's internal details for logging. */ #include "ble_hs_priv.h" #include "bletest_priv.h" #include #define BLETEST_TASK_PRIO 5 /* For LED toggling */ int g_led_pin; /* A buffer for host advertising data */ uint8_t g_host_adv_data[BLE_HCI_MAX_ADV_DATA_LEN]; uint8_t g_host_adv_len; static uint8_t _g_dev_addr[BLE_DEV_ADDR_LEN]; /* Some application configurations */ #define BLETEST_ROLE_NONE (0) #define BLETEST_ROLE_ADVERTISER (1) #define BLETEST_ROLE_SCANNER (2) #define BLETEST_ROLE_INITIATOR (3) #define MYNEWT_VAL_BLETEST_ROLE BLETEST_ROLE_ADVERTISER #if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_ADVERTISER #define BLETEST_CFG_ROLE BLETEST_ROLE_ADVERTISER #endif #if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_SCANNER #define BLETEST_CFG_ROLE BLETEST_ROLE_SCANNER #endif #if MYNEWT_VAL(BLETEST_ROLE) == BLETEST_ROLE_INITIATOR #define BLETEST_CFG_ROLE BLETEST_ROLE_INITIATOR #endif #ifndef BLETEST_CFG_ROLE #error "No role defined! Must define a valid role in syscfg.yml in apps/bletest" #endif /* Advertiser config */ #define BLETEST_CFG_ADV_OWN_ADDR_TYPE (BLE_HCI_ADV_OWN_ADDR_PUBLIC) #define BLETEST_CFG_ADV_PEER_ADDR_TYPE (BLE_HCI_ADV_PEER_ADDR_PUBLIC) #define BLETEST_CFG_ADV_ITVL (60000 / BLE_HCI_ADV_ITVL) #define BLETEST_CFG_ADV_TYPE BLE_HCI_ADV_TYPE_ADV_IND #define BLETEST_CFG_ADV_FILT_POLICY (BLE_HCI_ADV_FILT_NONE) #define BLETEST_CFG_ADV_ADDR_RES_EN (0) /* Multi-adv config */ /* * Number of advertising instances to start up, not including the default * instance. The default instance is used to connect. If this number is greater * than the number of available advertising instances, we only use the number * of available advertising instances (defined by the configuration setting: * BLE_MULTI_ADV_INSTANCES. */ #define BLETEST_CFG_ADV_TEST_INSTANCES (8) struct bletest_multi_adv_interval { uint8_t adv_type; /* * Note: if own addr type greater than 1, we use own addr field; otherwise * we use the set multi random address call to set the random address */ uint8_t adv_own_addr_type; uint16_t adv_itvl; }; /* * NOTE: currently, these are all NONCONN_IND. Thus, must be 100 msecs or * greater */ const struct bletest_multi_adv_interval bletest_multi_adv_instances[BLETEST_CFG_ADV_TEST_INSTANCES] = { {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND, BLE_HCI_ADV_OWN_ADDR_PUBLIC, (100000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_SCAN_IND, BLE_HCI_ADV_OWN_ADDR_RANDOM, (110000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND, BLE_HCI_ADV_OWN_ADDR_RANDOM, (120000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND, BLE_HCI_ADV_OWN_ADDR_PUBLIC, (130000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_SCAN_IND, BLE_HCI_ADV_OWN_ADDR_MAX + 1, (140000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND, BLE_HCI_ADV_OWN_ADDR_MAX + 1, (150000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_NONCONN_IND, BLE_HCI_ADV_OWN_ADDR_PUBLIC, (160000 / BLE_HCI_ADV_ITVL)}, {BLE_HCI_ADV_TYPE_ADV_SCAN_IND, BLE_HCI_ADV_OWN_ADDR_PUBLIC, (170000 / BLE_HCI_ADV_ITVL)} }; /* * Determines if own address contains random address or set through the * multi-adv set random address command */ #define BLETEST_CFG_MULTI_ADV_RANDOM_OWN (0) /* Scan config */ #define BLETEST_CFG_SCAN_ITVL (700000 / BLE_HCI_SCAN_ITVL) #define BLETEST_CFG_SCAN_WINDOW (700000 / BLE_HCI_SCAN_ITVL) #define BLETEST_CFG_SCAN_TYPE (BLE_HCI_SCAN_TYPE_PASSIVE) #define BLETEST_CFG_SCAN_OWN_ADDR_TYPE (BLE_HCI_ADV_OWN_ADDR_PUBLIC) #define BLETEST_CFG_SCAN_FILT_POLICY (BLE_HCI_SCAN_FILT_NO_WL) #define BLETEST_CFG_FILT_DUP_ADV (1) /* Connection config */ #define BLETEST_CFG_CONN_ITVL (128) /* in 1.25 msec increments */ #define BLETEST_CFG_SLAVE_LATENCY (0) #define BLETEST_CFG_INIT_FILTER_POLICY (BLE_HCI_CONN_FILT_NO_WL) #define BLETEST_CFG_CONN_SPVN_TMO (1000) /* 10 msec increments */ #define BLETEST_CFG_MIN_CE_LEN (6) #define BLETEST_CFG_MAX_CE_LEN (BLETEST_CFG_CONN_ITVL) #define BLETEST_CFG_CONN_PEER_ADDR_TYPE (BLE_HCI_CONN_PEER_ADDR_PUBLIC) #define BLETEST_CFG_CONN_OWN_ADDR_TYPE (BLE_HCI_ADV_OWN_ADDR_PUBLIC) #define BLETEST_CFG_CONCURRENT_CONNS (1) /* Test packet config */ #define BLETEST_CFG_RAND_PKT_SIZE (1) #define BLETEST_CFG_SUGG_DEF_TXOCTETS (251) #define BLETEST_CFG_SUGG_DEF_TXTIME (2000) /* Test configurations. One of these should be set to 1 */ #if !defined(BLETEST_CONCURRENT_CONN_TEST) && !defined(BLETEST_THROUGHPUT_TEST) #define BLETEST_CONCURRENT_CONN_TEST (1) #endif /* BLETEST variables */ #undef BLETEST_ADV_PKT_NUM #define BLETEST_MAX_PKT_SIZE (247) #define BLETEST_PKT_SIZE (247) #define BLETEST_STACK_SIZE (256) uint32_t g_next_os_time; int g_bletest_state; struct ble_npl_eventq g_bletest_evq; struct ble_npl_callout g_bletest_timer; //struct ble_npl_task bletest_task; //bssnz_t os_stack_t bletest_stack[BLETEST_STACK_SIZE]; uint32_t g_bletest_conn_end; int g_bletest_start_update; uint32_t g_bletest_conn_upd_time; uint8_t g_bletest_current_conns; uint8_t g_bletest_cur_peer_addr[BLE_DEV_ADDR_LEN]; uint8_t g_last_handle_used; uint8_t g_bletest_led_state; uint32_t g_bletest_led_rate; uint32_t g_bletest_next_led_time; uint16_t g_bletest_handle; uint16_t g_bletest_completed_pkts; uint16_t g_bletest_outstanding_pkts; uint16_t g_bletest_ltk_reply_handle; uint32_t g_bletest_hw_id[4]; struct hci_create_conn g_cc; /* --- For LE encryption testing --- */ /* Key: 0x4C68384139F574D836BCF34E9DFB01BF */ const uint8_t g_ble_ll_encrypt_test_key[16] = { 0x4c, 0x68, 0x38, 0x41, 0x39, 0xf5, 0x74, 0xd8, 0x36, 0xbc, 0xf3, 0x4e, 0x9d, 0xfb, 0x01, 0xbf }; /* Plaint text: 0x0213243546576879acbdcedfe0f10213 */ const uint8_t g_ble_ll_encrypt_test_plain_text[16] = { 0x02, 0x13, 0x24, 0x35, 0x46, 0x57, 0x68, 0x79, 0xac, 0xbd, 0xce, 0xdf, 0xe0, 0xf1, 0x02, 0x13 }; /* Encrypted data: 0x99ad1b5226a37e3e058e3b8e27c2c666 */ const uint8_t g_ble_ll_encrypt_test_encrypted_data[16] = { 0x99, 0xad, 0x1b, 0x52, 0x26, 0xa3, 0x7e, 0x3e, 0x05, 0x8e, 0x3b, 0x8e, 0x27, 0xc2, 0xc6, 0x66 }; #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) uint8_t g_bletest_adv_irk[16] = { 0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b }; uint8_t g_bletest_init_irk[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }; #endif #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) /* LTK 0x4C68384139F574D836BCF34E9DFB01BF */ const uint8_t g_bletest_LTK[16] = { 0x4C,0x68,0x38,0x41,0x39,0xF5,0x74,0xD8, 0x36,0xBC,0xF3,0x4E,0x9D,0xFB,0x01,0xBF }; uint16_t g_bletest_EDIV = 0x2474; uint64_t g_bletest_RAND = 0xABCDEF1234567890; uint64_t g_bletest_SKDm = 0xACBDCEDFE0F10213; uint64_t g_bletest_SKDs = 0x0213243546576879; uint32_t g_bletest_IVm = 0xBADCAB24; uint32_t g_bletest_IVs = 0xDEAFBABE; #endif #if (BLETEST_THROUGHPUT_TEST == 1) void bletest_completed_pkt(uint16_t handle) { os_sr_t sr; OS_ENTER_CRITICAL(sr); if (handle == g_bletest_handle) { ++g_bletest_completed_pkts; } OS_EXIT_CRITICAL(sr); } #endif #ifdef BLETEST_ADV_PKT_NUM void bletest_inc_adv_pkt_num(void) { int rc; uint8_t *dptr; uint8_t digit; if (g_host_adv_len != 0) { dptr = &g_host_adv_data[18]; while (dptr >= &g_host_adv_data[13]) { digit = *dptr; ++digit; if (digit == 58) { digit = 48; *dptr = digit; --dptr; } else { *dptr = digit; break; } } rc = bletest_hci_le_set_adv_data(g_host_adv_data, g_host_adv_len); assert(rc == 0); } } #endif /** * Sets the advertising data to be sent in advertising pdu's which contain * advertising data. * * @param dptr * @return uint8_t */ uint8_t bletest_set_adv_data(uint8_t *dptr, uint8_t *addr) { uint8_t len; /* Place flags in first */ dptr[0] = 0x02; dptr[1] = 0x01; /* Flags identifier */ dptr[2] = 0x06; dptr += 3; len = 3; /* Add HID service */ dptr[0] = 0x03; dptr[1] = 0x03; dptr[2] = 0x12; dptr[3] = 0x18; dptr += 4; len += 4; /* Add local name */ dptr[0] = 12; /* Length of this data, not including the length */ dptr[1] = 0x09; dptr[2] = 'r'; dptr[3] = 'u'; dptr[4] = 'n'; dptr[5] = 't'; dptr[6] = 'i'; dptr[7] = 'm'; dptr[8] = 'e'; dptr[9] = '-'; dptr[10] = '0'; dptr[11] = '0'; dptr[12] = '7'; dptr += 13; len += 13; /* Add local device address */ dptr[0] = 0x08; dptr[1] = 0x1B; dptr[2] = 0x00; memcpy(dptr + 3, addr, BLE_DEV_ADDR_LEN); len += 9; g_host_adv_len = len; return len; } #if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER) #if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT) void bletest_init_adv_instances(void) { uint8_t i; int rc; uint8_t *addr; uint8_t adv_len; uint8_t inst_allowed; uint8_t rand_addr[BLE_DEV_ADDR_LEN]; struct hci_multi_adv_params adv; inst_allowed = MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES); if (inst_allowed > BLETEST_CFG_ADV_TEST_INSTANCES) { inst_allowed = BLETEST_CFG_ADV_TEST_INSTANCES; } /* Start up all the instances */ for (i = 1; i <= inst_allowed; ++i) { memset(&adv, 0, sizeof(struct hci_multi_adv_params)); adv.own_addr_type = bletest_multi_adv_instances[i-1].adv_own_addr_type; if (adv.own_addr_type == BLE_HCI_ADV_OWN_ADDR_PUBLIC) { addr = _g_dev_addr; } else { memcpy(rand_addr, _g_dev_addr, BLE_DEV_ADDR_LEN); rand_addr[5] |= 0xc0; rand_addr[0] = i; /* * NOTE: we overload own address type with a special case * to denote if we use own address or call to set multi random * address. */ if (adv.own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) { rc = bletest_hci_le_set_multi_rand_addr(rand_addr, i); assert(rc == 0); addr = rand_addr; } else { adv.own_addr_type = BLE_HCI_ADV_OWN_ADDR_RANDOM; addr = rand_addr; memcpy(adv.own_addr, addr, BLE_DEV_ADDR_LEN); } } adv.adv_type = bletest_multi_adv_instances[i - 1].adv_type; adv.adv_channel_map = 0x07; adv.adv_filter_policy = BLE_HCI_ADV_FILT_NONE; adv.peer_addr_type = BLE_HCI_ADV_PEER_ADDR_PUBLIC; adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr); adv.adv_itvl_min = bletest_multi_adv_instances[i - 1].adv_itvl; adv.adv_itvl_max = bletest_multi_adv_instances[i - 1].adv_itvl; adv.adv_tx_pwr = -1 * i; /* Set the advertising parameters */ rc = bletest_hci_le_set_multi_adv_params(&adv, i); assert(rc == 0); /* Set advertising data */ if (adv_len != 0) { rc = bletest_hci_le_set_multi_adv_data(&g_host_adv_data[0], adv_len, i); assert(rc == 0); /* Set scan response data */ rc = bletest_hci_le_set_multi_scan_rsp_data(&g_host_adv_data[0], adv_len, i); assert(rc == 0); } /* Set the advertising parameters */ rc = bletest_hci_le_set_multi_adv_enable(1, i); assert(rc == 0); } } void bletest_init_advertising(uint8_t instance, int8_t txpwr) { int rc; int set_peer_addr; uint8_t adv_len; uint8_t *addr; uint8_t rand_addr[BLE_DEV_ADDR_LEN]; struct hci_multi_adv_params adv; /* Make sure it is a valid instance */ assert(instance < BLE_LL_ADV_INSTANCES); /* Just zero out advertising */ set_peer_addr = 0; memset(&adv, 0, sizeof(struct hci_multi_adv_params)); /* If we are using a random address, we need to set it */ adv.own_addr_type = BLETEST_CFG_ADV_OWN_ADDR_TYPE; if (adv.own_addr_type & 1) { memcpy(rand_addr, _g_dev_addr, BLE_DEV_ADDR_LEN); rand_addr[5] |= 0xc0; if (BLETEST_CFG_MULTI_ADV_RANDOM_OWN == 1) { addr = rand_addr; memcpy(adv.own_addr, addr, BLE_DEV_ADDR_LEN); } else { rc = bletest_hci_le_set_multi_rand_addr(rand_addr, instance); assert(rc == 0); addr = rand_addr; } } else { addr = _g_dev_addr; } /* Set advertising parameters */ adv.adv_type = BLETEST_CFG_ADV_TYPE; adv.adv_channel_map = 0x07; adv.adv_filter_policy = BLETEST_CFG_ADV_FILT_POLICY; if ((adv.adv_filter_policy & 1) || (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) { set_peer_addr = 1; } adv.peer_addr_type = BLETEST_CFG_ADV_PEER_ADDR_TYPE; if ((adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) || (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD)) { set_peer_addr = 1; adv_len = 0; } else { adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr); } /* Not allowed for multi-adv command */ if (adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { assert(0); } if (set_peer_addr) { memcpy(adv.peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN); if (adv.peer_addr_type == BLE_HCI_ADV_PEER_ADDR_RANDOM) { adv.peer_addr[5] |= 0xc0; } } console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n", adv.peer_addr[0], adv.peer_addr[1], adv.peer_addr[2], adv.peer_addr[3], adv.peer_addr[4], adv.peer_addr[5]); if (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { adv.adv_itvl_min = 0; adv.adv_itvl_max = 0; } else { adv.adv_itvl_min = BLETEST_CFG_ADV_ITVL; adv.adv_itvl_max = BLETEST_CFG_ADV_ITVL; /* Advertising interval */ } adv.adv_tx_pwr = txpwr; /* Set the advertising parameters */ rc = bletest_hci_le_set_multi_adv_params(&adv, instance); assert(rc == 0); /* Set advertising data */ if (adv_len != 0) { rc = bletest_hci_le_set_multi_adv_data(&g_host_adv_data[0], adv_len, instance); assert(rc == 0); /* Set scan response data */ rc = bletest_hci_le_set_multi_scan_rsp_data(&g_host_adv_data[0],adv_len, instance); assert(rc == 0); } } #else void bletest_init_advertising(void) { int rc; int set_peer_addr; uint8_t adv_len; uint8_t *addr; uint8_t rand_addr[BLE_DEV_ADDR_LEN]; struct hci_adv_params adv; /* Just zero out advertising */ set_peer_addr = 0; memset(&adv, 0, sizeof(struct hci_adv_params)); /* If we are using a random address, we need to set it */ adv.own_addr_type = BLETEST_CFG_ADV_OWN_ADDR_TYPE; if (adv.own_addr_type & 1) { memcpy(rand_addr, _g_dev_addr, BLE_DEV_ADDR_LEN); rand_addr[5] |= 0xc0; rc = bletest_hci_le_set_rand_addr(rand_addr); assert(rc == 0); addr = rand_addr; } else { addr = _g_dev_addr; } /* Set advertising parameters */ adv.adv_type = BLETEST_CFG_ADV_TYPE; adv.adv_channel_map = 0x07; adv.adv_filter_policy = BLETEST_CFG_ADV_FILT_POLICY; if ((adv.adv_filter_policy & 1) || (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) { set_peer_addr = 1; } adv.peer_addr_type = BLETEST_CFG_ADV_PEER_ADDR_TYPE; if ((adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) || (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD)) { set_peer_addr = 1; adv_len = 0; } else { adv_len = bletest_set_adv_data(&g_host_adv_data[0], addr); } if (adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { set_peer_addr = 1; } if (set_peer_addr) { memcpy(adv.peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN); if (adv.peer_addr_type == BLE_HCI_ADV_PEER_ADDR_RANDOM) { adv.peer_addr[5] |= 0xc0; } } console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n", adv.peer_addr[0], adv.peer_addr[1], adv.peer_addr[2], adv.peer_addr[3], adv.peer_addr[4], adv.peer_addr[5]); if (adv.adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { adv.adv_itvl_min = 0; adv.adv_itvl_max = 0; } else { adv.adv_itvl_min = BLETEST_CFG_ADV_ITVL; adv.adv_itvl_max = BLETEST_CFG_ADV_ITVL; /* Advertising interval */ } /* Set the advertising parameters */ rc = bletest_hci_le_set_adv_params(&adv); assert(rc == 0); #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) if ((adv.own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) || (BLETEST_CFG_ADV_ADDR_RES_EN == 1)) { rc = bletest_hci_le_add_resolv_list(g_bletest_adv_irk, g_bletest_init_irk, adv.peer_addr, adv.peer_addr_type); assert(rc == 0); rc = bletest_hci_le_enable_resolv_list(1); assert(rc == 0); } #endif /* Set advertising data */ if (adv_len != 0) { rc = bletest_hci_le_set_adv_data(&g_host_adv_data[0], adv_len); assert(rc == 0); /* Set scan response data */ rc = bletest_hci_le_set_scan_rsp_data(&g_host_adv_data[0], adv_len); assert(rc == 0); } } #endif /* MULTI_ADV SUPPORT */ #endif /* BLETEST_ROLE_ADVERTISER */ #if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER) void bletest_init_scanner(void) { int rc; uint8_t own_addr_type; uint8_t buf[BLE_HCI_SET_SCAN_PARAM_LEN]; uint8_t add_whitelist; own_addr_type = BLETEST_CFG_SCAN_OWN_ADDR_TYPE; rc = ble_hs_hci_cmd_build_le_set_scan_params(BLETEST_CFG_SCAN_TYPE, BLETEST_CFG_SCAN_ITVL, BLETEST_CFG_SCAN_WINDOW, own_addr_type, BLETEST_CFG_SCAN_FILT_POLICY, buf, sizeof buf); assert(rc == 0); rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_PARAMS), buf, sizeof(buf)); assert(rc == 0); if (rc == 0) { add_whitelist = BLETEST_CFG_SCAN_FILT_POLICY; #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) { rc = bletest_hci_le_add_resolv_list(g_bletest_init_irk, g_bletest_adv_irk, g_bletest_cur_peer_addr, BLETEST_CFG_ADV_PEER_ADDR_TYPE); assert(rc == 0); rc = bletest_hci_le_enable_resolv_list(1); assert(rc == 0); } #endif if (add_whitelist & 1) { rc = bletest_hci_le_add_to_whitelist(g_bletest_cur_peer_addr, BLE_ADDR_RANDOM); assert(rc == 0); } } } void bletest_execute_scanner(void) { int rc; /* Enable scanning */ if ((int32_t)(ble_npl_time_get() - g_next_os_time) >= 0) { if (g_bletest_state) { rc = bletest_hci_le_set_scan_enable(0, BLETEST_CFG_FILT_DUP_ADV); assert(rc == 0); g_bletest_state = 0; } else { rc = bletest_hci_le_set_scan_enable(1, BLETEST_CFG_FILT_DUP_ADV); assert(rc == 0); g_bletest_state = 1; } g_next_os_time += (OS_TICKS_PER_SEC * 60); } } #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR) void bletest_init_initiator(void) { int rc; uint8_t rand_addr[BLE_DEV_ADDR_LEN]; struct hci_create_conn *hcc; /* Enable initiating */ hcc = &g_cc; hcc->conn_itvl_max = BLETEST_CFG_CONN_ITVL; hcc->conn_itvl_min = BLETEST_CFG_CONN_ITVL; hcc->conn_latency = BLETEST_CFG_SLAVE_LATENCY; hcc->filter_policy = BLETEST_CFG_INIT_FILTER_POLICY; hcc->supervision_timeout = BLETEST_CFG_CONN_SPVN_TMO; hcc->scan_itvl = BLETEST_CFG_SCAN_ITVL; hcc->scan_window = BLETEST_CFG_SCAN_WINDOW; hcc->peer_addr_type = BLETEST_CFG_CONN_PEER_ADDR_TYPE; memcpy(hcc->peer_addr, g_bletest_cur_peer_addr, BLE_DEV_ADDR_LEN); if (hcc->peer_addr_type == BLE_HCI_CONN_PEER_ADDR_RANDOM) { hcc->peer_addr[5] |= 0xc0; } hcc->own_addr_type = BLETEST_CFG_CONN_OWN_ADDR_TYPE; hcc->min_ce_len = BLETEST_CFG_MIN_CE_LEN; hcc->max_ce_len = BLETEST_CFG_MAX_CE_LEN; console_printf("Trying to connect to %x.%x.%x.%x.%x.%x\n", hcc->peer_addr[0], hcc->peer_addr[1], hcc->peer_addr[2], hcc->peer_addr[3], hcc->peer_addr[4], hcc->peer_addr[5]); /* If we are using a random address, we need to set it */ if (hcc->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) { memcpy(rand_addr, _g_dev_addr, BLE_DEV_ADDR_LEN); rand_addr[5] |= 0xc0; rc = bletest_hci_le_set_rand_addr(rand_addr); assert(rc == 0); } #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) if ((hcc->peer_addr_type > BLE_HCI_CONN_PEER_ADDR_RANDOM) || (hcc->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM)) { rc = bletest_hci_le_add_resolv_list(g_bletest_init_irk, g_bletest_adv_irk, g_bletest_cur_peer_addr, BLETEST_CFG_ADV_PEER_ADDR_TYPE); assert(rc == 0); rc = bletest_hci_le_enable_resolv_list(1); assert(rc == 0); } #endif bletest_hci_le_create_connection(hcc); } void bletest_execute_initiator(void) { int i; int rc; int8_t rssi; uint16_t handle; uint8_t new_chan_map[5]; /* * Determine if there is an active connection for the current handle * we are trying to create. If so, start looking for the next one */ if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) { handle = g_bletest_current_conns + 1; if (ble_hs_conn_find(handle)) { /* Set LED to slower blink rate */ g_bletest_led_rate = OS_TICKS_PER_SEC; /* Ask for version information */ rc = bletest_hci_rd_rem_version(handle); /* Ask for remote used features */ rc = bletest_hci_le_read_rem_used_feat(handle); /* Add to current connections */ if (!rc) { ++g_bletest_current_conns; /* Move to next connection */ if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) { /* restart initiating */ g_bletest_cur_peer_addr[5] += 1; _g_dev_addr[5] += 1; bletest_init_initiator(); } } } else { bletest_hci_le_create_connection(&g_cc); } } else { if ((int32_t)(ble_npl_time_get() - g_next_os_time) >= 0) { if ((g_bletest_state == 1) || (g_bletest_state == 3)) { for (i = 0; i < g_bletest_current_conns; ++i) { if (ble_hs_conn_find(i + 1)) { bletest_hci_le_rd_chanmap(i+1); } } } else if (g_bletest_state == 2) { new_chan_map[0] = 0; new_chan_map[1] = 0x3; new_chan_map[2] = 0; new_chan_map[3] = 0x1F; new_chan_map[4] = 0; bletest_hci_le_set_host_chan_class(new_chan_map); } else if (g_bletest_state == 4) { #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) struct hci_start_encrypt hsle; for (i = 0; i < g_bletest_current_conns; ++i) { if (ble_hs_conn_find(i + 1)) { hsle.connection_handle = i + 1; hsle.encrypted_diversifier = g_bletest_EDIV; hsle.random_number = g_bletest_RAND; swap_buf(hsle.long_term_key, (uint8_t *)g_bletest_LTK, 16); bletest_hci_le_start_encrypt(&hsle); } } #endif } else if (g_bletest_state == 8) { #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) struct hci_start_encrypt hsle; for (i = 0; i < g_bletest_current_conns; ++i) { if (ble_hs_conn_find(i + 1)) { hsle.connection_handle = i + 1; hsle.encrypted_diversifier = g_bletest_EDIV; hsle.random_number = ~g_bletest_RAND; swap_buf(hsle.long_term_key, (uint8_t *)g_bletest_LTK, 16); bletest_hci_le_start_encrypt(&hsle); } } #endif } else { for (i = 0; i < g_bletest_current_conns; ++i) { if (ble_hs_conn_find(i + 1)) { ble_hs_hci_util_read_rssi(i+1, &rssi); } } } ++g_bletest_state; if (g_bletest_state > 9) { g_bletest_state = 9; } g_next_os_time = ble_npl_time_get() + OS_TICKS_PER_SEC * 3; } } } #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER) /* * Test wrapper to get packets. Only get a packet if we have more than half * left */ static struct os_mbuf * bletest_get_packet(void) { struct os_mbuf *om; om = NULL; if (os_msys_num_free() >= 5) { om = os_msys_get_pkthdr(0, sizeof(struct ble_mbuf_hdr)); } return om; } static struct os_mbuf * bletest_send_packet(uint16_t handle) { int j; uint8_t val; struct os_mbuf *om; uint16_t pktlen; om = bletest_get_packet(); if (om) { /* set payload length */ #if BLETEST_THROUGHPUT_TEST pktlen = BLETEST_PKT_SIZE; #else #if (BLETEST_CFG_RAND_PKT_SIZE == 1) pktlen = rand() % (BLETEST_MAX_PKT_SIZE + 1); #else pktlen = BLETEST_PKT_SIZE; #endif #endif /* Put the HCI header in the mbuf */ put_le16(om->om_data, handle); put_le16(om->om_data + 2, pktlen + 4); /* Place L2CAP header in packet */ put_le16(om->om_data + 4, pktlen); om->om_data[6] = 0; om->om_data[7] = 0; om->om_len = 8; OS_MBUF_PKTHDR(om)->omp_len = 8; /* Fill with incrementing pattern (starting from 1) */ for (j = 0; j < pktlen; ++j) { val = j + 1; os_mbuf_append(om, &val, 1); } /* Transmit it */ ble_hci_trans_hs_acl_tx(om); } return om; } static void bletest_execute_advertiser(void) { int i; #if (BLETEST_CONCURRENT_CONN_TEST == 1) #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) uint16_t mask; uint16_t reply_handle; #endif #endif int rc; uint16_t handle; struct os_mbuf *om; #if (BLETEST_THROUGHPUT_TEST == 1) os_sr_t sr; uint16_t completed_pkts; #endif /* See if we should start advertising again */ if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) { handle = g_bletest_current_conns + 1; if (ble_hs_conn_find(handle)) { /* Set LED to slower blink rate */ g_bletest_led_rate = OS_TICKS_PER_SEC; #if (BLETEST_THROUGHPUT_TEST == 1) /* Set next os time to 10 seconds after 1st connection */ if (g_next_os_time == 0) { g_next_os_time = os_time_get() + (10 * OS_TICKS_PER_SEC); g_bletest_handle = handle; } #endif /* Send the remote used features command */ rc = bletest_hci_le_read_rem_used_feat(handle); if (rc) { return; } /* Send the remote read version command */ rc = bletest_hci_rd_rem_version(handle); if (rc) { return; } /* set conn update time */ g_bletest_conn_upd_time = ble_npl_time_get() + (OS_TICKS_PER_SEC * 5); g_bletest_start_update = 1; /* Add to current connections */ ++g_bletest_current_conns; /* Move to next connection */ if (g_bletest_current_conns < BLETEST_CFG_CONCURRENT_CONNS) { /* restart initiating */ g_bletest_cur_peer_addr[5] += 1; _g_dev_addr[5] += 1; #if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT) bletest_init_advertising(0,0); bletest_hci_le_set_multi_adv_enable(1, 0); #else bletest_init_advertising(); bletest_hci_le_set_adv_enable(1); #endif } } else { /* If we failed to start advertising we should keep trying */ #if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT) bletest_hci_le_set_multi_adv_enable(1, 0); #else bletest_hci_le_set_adv_enable(1); #endif } } #if 0 if (g_bletest_start_update) { if ((int32_t)(os_time_get() - g_bletest_conn_upd_time) >= 0) { bletest_send_conn_update(1); g_bletest_start_update = 0; } } #endif #if (BLETEST_CONCURRENT_CONN_TEST == 1) /* See if it is time to hand a data packet to the connection */ if ((int32_t)(ble_npl_time_get() - g_next_os_time) >= 0) { #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) /* Do we need to send a LTK reply? */ mask = 1; reply_handle = 1; while (g_bletest_ltk_reply_handle && mask) { if (g_bletest_ltk_reply_handle & mask) { bletest_send_ltk_req_reply(reply_handle); //bletest_send_ltk_req_neg_reply(reply_handle); g_bletest_ltk_reply_handle &= ~mask; } ++reply_handle; mask <<= 1; } #endif if (g_bletest_current_conns) { for (i = 0; i < g_bletest_current_conns; ++i) { if ((g_last_handle_used == 0) || (g_last_handle_used > g_bletest_current_conns)) { g_last_handle_used = 1; } handle = g_last_handle_used; if (ble_hs_conn_find(handle)) { om = bletest_send_packet(handle); if (om) { /* Increment last handle used */ ++g_last_handle_used; } } else { ++g_last_handle_used; } } } g_next_os_time = ble_npl_time_get() + OS_TICKS_PER_SEC; } #endif #if (BLETEST_THROUGHPUT_TEST == 1) /* Nothing to do if no connections */ if (!g_bletest_current_conns) { return; } /* See if it is time to start throughput testing */ if ((int32_t)(os_time_get() - g_next_os_time) >= 0) { /* Keep window full */ OS_ENTER_CRITICAL(sr); completed_pkts = g_bletest_completed_pkts; g_bletest_completed_pkts = 0; OS_EXIT_CRITICAL(sr); assert(g_bletest_outstanding_pkts >= completed_pkts); g_bletest_outstanding_pkts -= completed_pkts; while (g_bletest_outstanding_pkts < 20) { om = bletest_send_packet(g_bletest_handle); if (om) { ++g_bletest_outstanding_pkts; } } } #endif /* XXX: throughput test */ } #endif /* XXX: BLETEST_ROLE_ADVERTISER */ /** * Main bletest function. Called by the task timer every 50 msecs. * */ void bletest_execute(void) { /* Toggle LED at set rate */ if ((int32_t)(ble_npl_time_get() - g_bletest_next_led_time) >= 0) { // hal_gpio_toggle(LED_BLINK_PIN); g_bletest_next_led_time = ble_npl_time_get() + g_bletest_led_rate; } #if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER) bletest_execute_advertiser(); #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER) bletest_execute_scanner(); #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR) bletest_execute_initiator(); #endif } /** * Callback when BLE test timer expires. * * @param arg */ void bletest_timer_cb(struct ble_npl_event *ev) { /* Call the bletest code */ bletest_execute(); /* Re-start the timer (run every 10 msecs) */ ble_npl_callout_reset(&g_bletest_timer, OS_TICKS_PER_SEC / 100); } /** * BLE test task * * @param arg */ void bletest_task_handler(void *arg) { int rc; uint64_t rand64; uint64_t event_mask; /* Set LED blink rate */ g_bletest_led_rate = OS_TICKS_PER_SEC / 20; /* Wait one second before starting test task */ ble_npl_time_delay(OS_TICKS_PER_SEC); /* Initialize the host timer */ ble_npl_callout_init(&g_bletest_timer, &g_bletest_evq, bletest_timer_cb, NULL); ble_hs_dbg_set_sync_state(BLE_HS_SYNC_STATE_GOOD); /* Send the reset command first */ rc = bletest_hci_reset_ctlr(); assert(rc == 0); #if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER) /* Initialize the advertiser */ console_printf("Starting BLE test task as advertiser\n"); #if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT) /* Start up all advertising instances except default one */ bletest_init_adv_instances(); /* Start advertising on instance 0 at 0 dbm */ bletest_init_advertising(0, 0); #else bletest_init_advertising(); #endif #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_SCANNER) /* Initialize the scanner */ console_printf("Starting BLE test task as scanner\n"); bletest_init_scanner(); #endif #if (BLETEST_CFG_ROLE == BLETEST_ROLE_INITIATOR) /* Initialize the scanner */ console_printf("Starting BLE test task as initiator\n"); bletest_init_initiator(); #endif /* Read unique HW id */ // rc = hal_bsp_hw_id((void *)&g_bletest_hw_id[0], sizeof(g_bletest_hw_id)); // assert(rc == 16); console_printf("HW id=%04x%04x%04x%04x\n", (unsigned int)g_bletest_hw_id[0], (unsigned int)g_bletest_hw_id[1], (unsigned int)g_bletest_hw_id[2], (unsigned int)g_bletest_hw_id[3]); /* Set the event mask we want to display */ event_mask = 0x7FF; rc = bletest_hci_le_set_event_mask(event_mask); assert(rc == 0); /* Turn on all events */ event_mask = 0xffffffffffffffff; rc = bletest_hci_set_event_mask(event_mask); assert(rc == 0); /* Read device address */ rc = bletest_hci_rd_bd_addr(); assert(rc == 0); /* Read local features */ rc = bletest_hci_rd_local_feat(); assert(rc == 0); /* Read local commands */ rc = bletest_hci_rd_local_supp_cmd(); assert(rc == 0); /* Read version */ rc = bletest_hci_rd_local_version(); assert(rc == 0); /* Read supported states */ rc = bletest_hci_le_read_supp_states(); assert(rc == 0); /* Read maximum data length */ rc = bletest_hci_le_rd_max_datalen(); assert(rc == 0); #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT) == 1) /* Read suggested data length */ rc = bletest_hci_le_rd_sugg_datalen(); assert(rc == 0); /* write suggested default data length */ rc = bletest_hci_le_write_sugg_datalen(BLETEST_CFG_SUGG_DEF_TXOCTETS, BLETEST_CFG_SUGG_DEF_TXTIME); assert(rc == 0); /* Read suggested data length */ rc = bletest_hci_le_rd_sugg_datalen(); assert(rc == 0); /* Set data length (note: we know there is no connection; just a test) */ rc = bletest_hci_le_set_datalen(0x1234, BLETEST_CFG_SUGG_DEF_TXOCTETS, BLETEST_CFG_SUGG_DEF_TXTIME); assert(rc != 0); #endif /* Encrypt a block */ #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) rc = bletest_hci_le_encrypt((uint8_t *)g_ble_ll_encrypt_test_key, (uint8_t *)g_ble_ll_encrypt_test_plain_text); assert(rc == 0); #endif /* Get a random number */ rc = ble_hs_hci_util_rand(&rand64, 8); assert(rc == 0); /* Wait some time before starting */ ble_npl_time_delay(OS_TICKS_PER_SEC); /* Init state */ g_bletest_state = 0; /* Begin advertising if we are an advertiser */ #if (BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER) #if MYNEWT_VAL(BLE_ANDROID_MULTI_ADV_SUPPORT) rc = bletest_hci_le_set_multi_adv_enable(1, 0); assert(rc == 0); #else rc = bletest_hci_le_set_adv_enable(1); assert(rc == 0); #endif #endif bletest_timer_cb(NULL); } extern int nimble_ble_enable(void); /** * main * * The main task for the project. This function initializes the packages, * then starts serving events from default event queue. * * @return int NOTE: this function should never return! */ static int bletest(void) { rt_thread_t tid; /* startup bluetooth host stack*/ ble_hs_thread_startup(); /* Dummy device address */ #if BLETEST_CFG_ROLE == BLETEST_ROLE_ADVERTISER _g_dev_addr[0] = 0x00; _g_dev_addr[1] = 0x00; _g_dev_addr[2] = 0x00; _g_dev_addr[3] = 0x88; _g_dev_addr[4] = 0x88; _g_dev_addr[5] = 0x08; g_bletest_cur_peer_addr[0] = 0x00; g_bletest_cur_peer_addr[1] = 0x00; g_bletest_cur_peer_addr[2] = 0x00; g_bletest_cur_peer_addr[3] = 0x99; g_bletest_cur_peer_addr[4] = 0x99; g_bletest_cur_peer_addr[5] = 0x09; #else _g_dev_addr[0] = 0x00; _g_dev_addr[1] = 0x00; _g_dev_addr[2] = 0x00; _g_dev_addr[3] = 0x99; _g_dev_addr[4] = 0x99; _g_dev_addr[5] = 0x09; g_bletest_cur_peer_addr[0] = 0x00; g_bletest_cur_peer_addr[1] = 0x00; g_bletest_cur_peer_addr[2] = 0x00; g_bletest_cur_peer_addr[3] = 0x88; g_bletest_cur_peer_addr[4] = 0x88; g_bletest_cur_peer_addr[5] = 0x08; #endif /* Set the led pin as an output */ // g_led_pin = LED_BLINK_PIN; // hal_gpio_init_out(g_led_pin, 1); /* Initialize eventq for bletest task */ ble_npl_eventq_init(&g_bletest_evq); tid = rt_thread_create("ble_test", bletest_task_handler, RT_NULL, 1024, 10, 10); if(tid != RT_NULL) rt_thread_startup(tid); return 0; } MSH_CMD_EXPORT_ALIAS(bletest, bletest, "bluetooth test sample");