/* * 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 #include "testutil/testutil.h" #include "nimble/ble.h" #include "nimble/hci_common.h" #include "host/ble_hs_adv.h" #include "ble_hs_test.h" #include "ble_hs_test_util.h" #define BLE_HCI_SET_SCAN_PARAM_LEN (7) #define BLE_HCI_SET_SCAN_ENABLE_LEN (2) #define BLE_HCI_DISCONNECT_CMD_LEN (3) #define BLE_HCI_SET_ADV_PARAM_LEN (15) #define BLE_HCI_SET_ADV_ENABLE_LEN (1) #define BLE_HCI_CONN_UPDATE_LEN (14) #define BLE_HCI_CONN_PARAM_REPLY_LEN (14) #define BLE_HCI_CONN_PARAM_NEG_REPLY_LEN (3) static struct ble_gap_event ble_gap_test_event; static int ble_gap_test_conn_status; static struct ble_gap_conn_desc ble_gap_test_conn_desc; static void *ble_gap_test_conn_arg; static struct ble_gap_upd_params ble_gap_test_conn_peer_params; static struct ble_gap_upd_params ble_gap_test_conn_self_params; static int ble_gap_test_disc_event_type; static struct ble_gap_disc_desc ble_gap_test_disc_desc; static void *ble_gap_test_disc_arg; /***************************************************************************** * $misc * *****************************************************************************/ static void ble_gap_test_util_reset_cb_info(void) { memset(&ble_gap_test_event, 0xff, sizeof ble_gap_test_event); ble_gap_test_conn_status = -1; memset(&ble_gap_test_conn_desc, 0xff, sizeof ble_gap_test_conn_desc); ble_gap_test_conn_arg = (void *)-1; ble_gap_test_disc_event_type = -1; memset(&ble_gap_test_disc_desc, 0xff, sizeof ble_gap_test_disc_desc); ble_gap_test_disc_arg = (void *)-1; } static void ble_gap_test_util_init(void) { ble_hs_test_util_init(); ble_hs_test_util_set_static_rnd_addr((uint8_t[6]){ 1, 2, 3, 4, 5, 0xc0 }); ble_gap_test_util_reset_cb_info(); } static int ble_gap_test_util_disc_cb(struct ble_gap_event *event, void *arg) { ble_gap_test_disc_event_type = event->type; ble_gap_test_disc_arg = arg; if (event->type == BLE_GAP_EVENT_DISC) { ble_gap_test_disc_desc = event->disc; } return 0; } static int ble_gap_test_util_connect_cb(struct ble_gap_event *event, void *arg) { int *fail_reason; int ret; ble_gap_test_event = *event; ble_gap_test_conn_arg = arg; switch (event->type) { case BLE_GAP_EVENT_CONNECT: ble_gap_test_conn_status = event->connect.status; ret = ble_gap_conn_find(event->connect.conn_handle, &ble_gap_test_conn_desc); TEST_ASSERT_FATAL(ble_gap_test_conn_status || ret == 0); break; case BLE_GAP_EVENT_DISCONNECT: ble_gap_test_conn_status = event->disconnect.reason; ble_gap_test_conn_desc = event->disconnect.conn; break; case BLE_GAP_EVENT_CONN_UPDATE: ble_gap_test_conn_status = event->conn_update.status; ret = ble_gap_conn_find(event->conn_update.conn_handle, &ble_gap_test_conn_desc); TEST_ASSERT_FATAL(ret == 0); break; case BLE_GAP_EVENT_TERM_FAILURE: ble_gap_test_conn_status = event->term_failure.status; ret = ble_gap_conn_find(event->term_failure.conn_handle, &ble_gap_test_conn_desc); TEST_ASSERT_FATAL(ret == 0); break; case BLE_GAP_EVENT_ADV_COMPLETE: ble_gap_test_conn_arg = arg; break; case BLE_GAP_EVENT_CONN_UPDATE_REQ: ble_gap_test_conn_peer_params = *event->conn_update_req.peer_params; *event->conn_update_req.self_params = ble_gap_test_conn_self_params; ret = ble_gap_conn_find(event->conn_update_req.conn_handle, &ble_gap_test_conn_desc); TEST_ASSERT_FATAL(ret == 0); fail_reason = arg; if (fail_reason == NULL) { return 0; } else { return *fail_reason; } break; case BLE_GAP_EVENT_MTU: break; default: TEST_ASSERT_FATAL(0); break; } return 0; } static int ble_gap_test_util_copy_cb(struct ble_gap_event *event, void *arg) { ble_gap_test_event = *event; ble_gap_test_conn_arg = arg; return 0; } static void ble_gap_test_util_verify_tx_clear_wl(void) { uint8_t param_len; ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLEAR_WHITE_LIST, ¶m_len); TEST_ASSERT(param_len == 0); } static void ble_gap_test_util_verify_tx_add_wl(ble_addr_t *addr) { uint8_t param_len; uint8_t *param; int i; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_WHITE_LIST, ¶m_len); TEST_ASSERT(param_len == 7); TEST_ASSERT(param[0] == addr->type); for (i = 0; i < 6; i++) { TEST_ASSERT(param[1 + i] == addr->val[i]); } } static void ble_gap_test_util_verify_tx_set_scan_params(uint8_t own_addr_type, uint8_t scan_type, uint16_t itvl, uint16_t scan_window, uint8_t filter_policy) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_PARAMS, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_SET_SCAN_PARAM_LEN); TEST_ASSERT(param[0] == scan_type); TEST_ASSERT(get_le16(param + 1) == itvl); TEST_ASSERT(get_le16(param + 3) == scan_window); TEST_ASSERT(param[5] == own_addr_type); TEST_ASSERT(param[6] == filter_policy); } static void ble_gap_test_util_verify_tx_scan_enable(uint8_t enable, uint8_t filter_duplicates) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_ENABLE, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_SET_SCAN_ENABLE_LEN); TEST_ASSERT(param[0] == enable); TEST_ASSERT(param[1] == filter_duplicates); } static void ble_hs_test_util_hci_verify_tx_create_conn_cancel(void) { uint8_t param_len; ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN_CANCEL, ¶m_len); TEST_ASSERT(param_len == 0); } static void ble_gap_test_util_verify_tx_disconnect(void) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LINK_CTRL, BLE_HCI_OCF_DISCONNECT_CMD, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_DISCONNECT_CMD_LEN); TEST_ASSERT(get_le16(param + 0) == 2); TEST_ASSERT(param[2] == BLE_ERR_REM_USER_CONN_TERM); } static void ble_gap_test_util_verify_tx_adv_params(void) { uint8_t param_len; ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_PARAMS, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_SET_ADV_PARAM_LEN); /* Note: Content of message verified in ble_hs_adv_test.c. */ } static void ble_gap_test_util_verify_tx_adv_data(void) { uint8_t param_len; ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_DATA, ¶m_len); /* Note: Content of message verified in ble_hs_adv_test.c. */ } #if 0 static void ble_gap_test_util_verify_tx_rsp_data(void) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA, ¶m_len); (void)param; /* XXX: Verify other fields. */ } #endif static void ble_gap_test_util_verify_tx_adv_enable(int enabled) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_ENABLE, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_SET_ADV_ENABLE_LEN); TEST_ASSERT(param[0] == !!enabled); } static void ble_gap_test_util_verify_tx_update_conn(struct ble_gap_upd_params *params) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CONN_UPDATE, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_CONN_UPDATE_LEN); TEST_ASSERT(get_le16(param + 0) == 2); TEST_ASSERT(get_le16(param + 2) == params->itvl_min); TEST_ASSERT(get_le16(param + 4) == params->itvl_max); TEST_ASSERT(get_le16(param + 6) == params->latency); TEST_ASSERT(get_le16(param + 8) == params->supervision_timeout); TEST_ASSERT(get_le16(param + 10) == params->min_ce_len); TEST_ASSERT(get_le16(param + 12) == params->max_ce_len); } static void ble_gap_test_util_verify_tx_params_reply_pos(void) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_RR, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_CONN_PARAM_REPLY_LEN); TEST_ASSERT(get_le16(param + 0) == 2); TEST_ASSERT(get_le16(param + 2) == ble_gap_test_conn_self_params.itvl_min); TEST_ASSERT(get_le16(param + 4) == ble_gap_test_conn_self_params.itvl_max); TEST_ASSERT(get_le16(param + 6) == ble_gap_test_conn_self_params.latency); TEST_ASSERT(get_le16(param + 8) == ble_gap_test_conn_self_params.supervision_timeout); TEST_ASSERT(get_le16(param + 10) == ble_gap_test_conn_self_params.min_ce_len); TEST_ASSERT(get_le16(param + 12) == ble_gap_test_conn_self_params.max_ce_len); } static void ble_gap_test_util_verify_tx_params_reply_neg(uint8_t reason) { uint8_t param_len; uint8_t *param; param = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR, ¶m_len); TEST_ASSERT(param_len == BLE_HCI_CONN_PARAM_NEG_REPLY_LEN); TEST_ASSERT(get_le16(param + 0) == 2); TEST_ASSERT(param[2] == reason); } static void ble_gap_test_util_rx_update_complete( uint8_t status, const struct ble_gap_upd_params *params) { struct ble_hci_ev_le_subev_conn_upd_complete evt; evt.subev_code = BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE; evt.status = status; evt.conn_handle = htole16(2); evt.conn_itvl = htole16(params->itvl_max); evt.conn_latency = htole16(params->latency); evt.supervision_timeout = htole16(params->supervision_timeout); ble_gap_rx_update_complete(&evt); } static int ble_gap_test_util_rx_param_req(struct ble_gap_upd_params *params, int pos, int *cmd_idx, int cmd_fail_idx, uint8_t fail_status) { struct ble_hci_ev_le_subev_rem_conn_param_req evt; uint16_t opcode; uint8_t hci_status; evt.subev_code = BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ; evt.conn_handle = htole16(2); evt.min_interval = htole16(params->itvl_min); evt.max_interval = htole16(params->itvl_max); evt.latency = htole16(params->latency); evt.timeout = params->supervision_timeout; if (pos) { opcode = ble_hs_hci_util_opcode_join( BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_RR); } else { opcode = ble_hs_hci_util_opcode_join( BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR); } if (*cmd_idx == cmd_fail_idx) { hci_status = fail_status; } else { hci_status = 0; } (*cmd_idx)++; ble_hs_test_util_hci_ack_set(opcode, hci_status); ble_gap_rx_param_req(&evt); return hci_status; } /***************************************************************************** * $white list * *****************************************************************************/ static void ble_gap_test_util_wl_set(ble_addr_t *addrs, int addrs_count, int cmd_fail_idx, uint8_t fail_status) { int cmd_idx; int rc; int i; ble_gap_test_util_init(); cmd_idx = 0; rc = ble_hs_test_util_wl_set(addrs, addrs_count, cmd_fail_idx, fail_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(fail_status)); /* Verify tx of clear white list command. */ ble_gap_test_util_verify_tx_clear_wl(); if (cmd_idx >= cmd_fail_idx) { return; } cmd_idx++; /* Verify tx of add white list commands. */ for (i = 0; i < addrs_count; i++) { ble_gap_test_util_verify_tx_add_wl(addrs + i); if (cmd_idx >= cmd_fail_idx) { return; } cmd_idx++; } } TEST_CASE_SELF(ble_gap_test_case_wl_bad_args) { int rc; ble_gap_test_util_init(); /*** 0 white list entries. */ rc = ble_hs_test_util_wl_set(NULL, 0, 0, 0); TEST_ASSERT(rc == BLE_HS_EINVAL); /*** Invalid address type. */ rc = ble_hs_test_util_wl_set( ((ble_addr_t[]) { { 5, { 1, 2, 3, 4, 5, 6 } }, }), 1, 0, 0); TEST_ASSERT(rc == BLE_HS_EINVAL); /*** White-list-using connection in progress. */ rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, NULL, 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT(rc == 0); rc = ble_hs_test_util_wl_set( ((ble_addr_t[]) { { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, }), 1, 0, 0); TEST_ASSERT(rc == BLE_HS_EBUSY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_wl_ctlr_fail) { int i; ble_addr_t addrs[] = { { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } }, { BLE_ADDR_PUBLIC, { 3, 4, 5, 6, 7, 8 } }, { BLE_ADDR_PUBLIC, { 4, 5, 6, 7, 8, 9 } }, }; int addrs_count = sizeof addrs / sizeof addrs[0]; for (i = 0; i < 5; i++) { ble_gap_test_util_wl_set(addrs, addrs_count, i, BLE_ERR_UNSPECIFIED); } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_wl_good) { ble_addr_t addrs[] = { { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } }, { BLE_ADDR_PUBLIC, { 3, 4, 5, 6, 7, 8 } }, { BLE_ADDR_PUBLIC, { 4, 5, 6, 7, 8, 9 } }, }; int addrs_count = sizeof addrs / sizeof addrs[0]; ble_gap_test_util_wl_set(addrs, addrs_count, 0, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_wl) { ble_gap_test_case_wl_good(); ble_gap_test_case_wl_bad_args(); ble_gap_test_case_wl_ctlr_fail(); } /***************************************************************************** * $discovery * *****************************************************************************/ static int ble_gap_test_util_disc(uint8_t own_addr_type, const struct ble_gap_disc_params *disc_params, struct ble_gap_disc_desc *desc, int cmd_fail_idx, uint8_t fail_status) { int rc; ble_gap_test_util_init(); TEST_ASSERT(!ble_gap_disc_active()); /* Begin the discovery procedure. */ rc = ble_hs_test_util_disc(own_addr_type, BLE_HS_FOREVER, disc_params, ble_gap_test_util_disc_cb, NULL, cmd_fail_idx, fail_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(fail_status)); if (rc == 0) { TEST_ASSERT(ble_gap_master_in_progress()); ble_gap_rx_adv_report(desc); } else { TEST_ASSERT(ble_gap_test_disc_event_type == -1); } if (cmd_fail_idx > 0) { /* Verify tx of set scan parameters command. */ ble_gap_test_util_verify_tx_set_scan_params( own_addr_type, disc_params->passive ? BLE_HCI_SCAN_TYPE_PASSIVE : BLE_HCI_SCAN_TYPE_ACTIVE, disc_params->itvl, disc_params->window, disc_params->filter_policy); } if (cmd_fail_idx > 1) { /* Verify tx of scan enable command. */ ble_gap_test_util_verify_tx_scan_enable( 1, disc_params->filter_duplicates); } if (rc == 0) { TEST_ASSERT(ble_gap_disc_active()); } return rc; } TEST_CASE_SELF(ble_gap_test_case_disc_bad_args) { struct ble_gap_disc_params params; int rc; params.itvl = 0; params.window = 0; params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; params.limited = 0; params.passive = 0; params.filter_duplicates = 0; ble_gap_test_util_init(); /*** Invalid filter policy. */ params.filter_policy = 6; rc = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, 0, ¶ms, ble_gap_test_util_disc_cb, NULL); TEST_ASSERT(rc == BLE_HS_EINVAL); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_good) { uint8_t adv_data[32]; uint8_t flags; uint8_t own_addr_type; int passive; int limited; int rc; struct ble_gap_disc_desc desc = { .event_type = BLE_HCI_ADV_TYPE_ADV_IND, .addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, .length_data = 0, .rssi = 0, .data = adv_data, }; struct ble_gap_disc_params disc_params = { .itvl = BLE_GAP_SCAN_SLOW_INTERVAL1, .window = BLE_GAP_SCAN_SLOW_WINDOW1, .filter_policy = BLE_HCI_CONN_FILT_NO_WL, .limited = 0, .passive = 0, .filter_duplicates = 0, }; flags = BLE_HS_ADV_F_DISC_LTD; rc = ble_hs_adv_set_flat(BLE_HS_ADV_TYPE_FLAGS, 1, &flags, adv_data, &desc.length_data, sizeof adv_data); TEST_ASSERT_FATAL(rc == 0); for (own_addr_type = 0; own_addr_type <= BLE_OWN_ADDR_RPA_RANDOM_DEFAULT; own_addr_type++) for (passive = 0; passive <= 1; passive++) for (limited = 0; limited <= 1; limited++) { disc_params.passive = passive; disc_params.limited = limited; ble_gap_test_util_disc(own_addr_type, &disc_params, &desc, -1, 0); TEST_ASSERT(ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_test_disc_event_type == BLE_GAP_EVENT_DISC); TEST_ASSERT(ble_gap_test_disc_desc.event_type == BLE_HCI_ADV_TYPE_ADV_IND); TEST_ASSERT(ble_gap_test_disc_desc.addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(ble_gap_test_disc_desc.length_data == 3); TEST_ASSERT(ble_gap_test_disc_desc.rssi == 0); TEST_ASSERT(memcmp(ble_gap_test_disc_desc.addr.val, desc.addr.val, 6) == 0); TEST_ASSERT(ble_gap_test_disc_arg == NULL); } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_ltd_mismatch) { int rc; struct ble_gap_disc_desc desc_gen = { .event_type = BLE_HCI_ADV_TYPE_ADV_IND, .length_data = 3, .rssi = 0, .addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, .data = (uint8_t[BLE_HS_ADV_MAX_SZ]){ 2, BLE_HS_ADV_TYPE_FLAGS, BLE_HS_ADV_F_DISC_GEN, }, }; struct ble_gap_disc_desc desc_lim = { .event_type = BLE_HCI_ADV_TYPE_ADV_IND, .length_data = 3, .rssi = 0, .addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, .data = (uint8_t[BLE_HS_ADV_MAX_SZ]){ 2, BLE_HS_ADV_TYPE_FLAGS, BLE_HS_ADV_F_DISC_LTD, }, }; struct ble_gap_disc_params disc_params = { .itvl = BLE_GAP_SCAN_SLOW_INTERVAL1, .window = BLE_GAP_SCAN_SLOW_WINDOW1, .filter_policy = BLE_HCI_CONN_FILT_NO_WL, .limited = 1, .passive = 0, .filter_duplicates = 0, }; rc = ble_gap_test_util_disc(BLE_OWN_ADDR_PUBLIC, &disc_params, &desc_gen, -1, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_master_in_progress()); /* Verify that the report was ignored because of a mismatched LTD flag. */ TEST_ASSERT(ble_gap_test_disc_event_type == -1); /* Stop the scan and swap the flags. */ rc = ble_hs_test_util_disc_cancel(0); TEST_ASSERT(rc == 0); disc_params.limited = 0; rc = ble_gap_test_util_disc(BLE_OWN_ADDR_PUBLIC, &disc_params, &desc_lim, -1, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_master_in_progress()); /* This time we should have reported the advertisement; general discovery * hears everything. */ TEST_ASSERT(ble_gap_test_disc_event_type == BLE_GAP_EVENT_DISC); } TEST_CASE_SELF(ble_gap_test_case_disc_hci_fail) { int fail_idx; int limited; int rc; struct ble_gap_disc_desc desc = { .event_type = BLE_HCI_ADV_TYPE_ADV_IND, .length_data = 0, .rssi = 0, .addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, .data = NULL, }; struct ble_gap_disc_params disc_params = { .itvl = BLE_GAP_SCAN_SLOW_INTERVAL1, .window = BLE_GAP_SCAN_SLOW_WINDOW1, .filter_policy = BLE_HCI_CONN_FILT_NO_WL, .limited = 0, .passive = 0, .filter_duplicates = 0, }; for (limited = 0; limited <= 1; limited++) { disc_params.limited = limited; for (fail_idx = 0; fail_idx < 2; fail_idx++) { rc = ble_gap_test_util_disc(BLE_OWN_ADDR_PUBLIC, &disc_params, &desc, fail_idx, BLE_ERR_UNSUPPORTED); TEST_ASSERT(rc == BLE_HS_HCI_ERR(BLE_ERR_UNSUPPORTED)); TEST_ASSERT(!ble_gap_master_in_progress()); } } ble_hs_test_util_assert_mbufs_freed(NULL); } static void ble_gap_test_util_disc_dflts_once(int limited) { struct ble_gap_disc_params params; uint16_t exp_window; uint16_t exp_itvl; int rc; ble_gap_test_util_init(); memset(¶ms, 0, sizeof params); params.limited = limited; rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, 0, ¶ms, ble_gap_test_util_disc_cb, NULL, -1, 0); TEST_ASSERT_FATAL(rc == 0); if (limited) { exp_itvl = BLE_GAP_LIM_DISC_SCAN_INT; exp_window = BLE_GAP_LIM_DISC_SCAN_WINDOW; } else { exp_itvl = BLE_GAP_SCAN_FAST_INTERVAL_MIN; exp_window = BLE_GAP_SCAN_FAST_WINDOW; } ble_gap_test_util_verify_tx_set_scan_params( BLE_OWN_ADDR_PUBLIC, BLE_HCI_SCAN_TYPE_ACTIVE, exp_itvl, exp_window, BLE_HCI_SCAN_FILT_NO_WL); ble_gap_test_util_verify_tx_scan_enable(1, 0); } TEST_CASE_SELF(ble_gap_test_case_disc_dflts) { ble_gap_test_util_disc_dflts_once(0); ble_gap_test_util_disc_dflts_once(1); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_already) { static const struct ble_gap_disc_params disc_params = { 0 }; int rc; ble_gap_test_util_init(); /* Start a discovery procedure. */ rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, &disc_params, ble_gap_test_util_disc_cb, NULL, -1, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure host indicates BLE_HS_EALREADY if we try to discover. */ rc = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, &disc_params, ble_gap_test_util_disc_cb, NULL); TEST_ASSERT(rc == BLE_HS_EALREADY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_busy) { static const struct ble_gap_disc_params disc_params = { 0 }; static const ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }; int rc; ble_gap_test_util_init(); /* Start a connect procedure. */ rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure host indicates BLE_HS_EBUSY if we try to discover. */ rc = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, &disc_params, ble_gap_test_util_disc_cb, NULL); TEST_ASSERT(rc == BLE_HS_EBUSY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_disc) { ble_gap_test_case_disc_bad_args(); ble_gap_test_case_disc_good(); ble_gap_test_case_disc_ltd_mismatch(); ble_gap_test_case_disc_hci_fail(); ble_gap_test_case_disc_dflts(); ble_gap_test_case_disc_already(); ble_gap_test_case_disc_busy(); } /***************************************************************************** * $direct connect * *****************************************************************************/ TEST_CASE_SELF(ble_gap_test_case_conn_gen_good) { struct ble_gap_conn_complete evt; struct ble_gap_conn_params params; int rc; ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; ble_gap_test_util_init(); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_conn_active()); params.scan_itvl = 0x12; params.scan_window = 0x11; params.itvl_min = 25; params.itvl_max = 26; params.latency = 1; params.supervision_timeout = 20; params.min_ce_len = 3; params.max_ce_len = 4; rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, 0, ¶ms, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_conn_active()); TEST_ASSERT(ble_gap_master_in_progress()); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == BLE_HS_ENOTCONN); /* ble_gap_rx_conn_complete() will send extra HCI command, need phony * ack */ ble_hs_test_util_hci_ack_set(ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_REM_FEAT), 0); /* Receive connection complete event. */ memset(&evt, 0, sizeof evt); evt.status = BLE_ERR_SUCCESS; evt.connection_handle = 2; evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER; memcpy(evt.peer_addr, peer_addr.val, 6); rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr.val, 6) == 0); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_bad_args) { int rc; ble_gap_test_util_init(); TEST_ASSERT(!ble_gap_master_in_progress()); /*** Invalid address type. */ rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &((ble_addr_t) { 5, { 1, 2, 3, 4, 5, 6 }}), 0, NULL, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(rc == BLE_HS_EINVAL); TEST_ASSERT(!ble_gap_master_in_progress()); /*** Connection already in progress. */ rc = ble_hs_test_util_connect( BLE_OWN_ADDR_PUBLIC, &((ble_addr_t) { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}), 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_master_in_progress()); rc = ble_gap_connect( BLE_OWN_ADDR_PUBLIC, &((ble_addr_t) { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}), 0, NULL, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(rc == BLE_HS_EALREADY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_dflt_params) { static const ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 2, 3, 8, 6, 6, 1 } }; int rc; ble_gap_test_util_init(); rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT(rc == 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_already) { static const struct ble_gap_conn_params conn_params = { 0 }; static const ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }; int rc; ble_gap_test_util_init(); /* Start a connect procedure. */ rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure host indicates BLE_HS_EALREADY if we try to connect. */ rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, BLE_HS_FOREVER, &conn_params, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(rc == BLE_HS_EALREADY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_done) { static const struct ble_gap_conn_params conn_params = { 0 }; static const ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }; int rc; ble_gap_test_util_init(); /* Successfully connect to the peer. */ ble_hs_test_util_create_conn(2, peer_addr.val, ble_gap_test_util_connect_cb, NULL); /* Ensure host indicates BLE_HS_EDONE if we try to connect to the same * peer. */ rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, BLE_HS_FOREVER, &conn_params, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(rc == BLE_HS_EDONE); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_busy) { static const struct ble_gap_disc_params disc_params = { 0 }; static const struct ble_gap_conn_params conn_params = { 0 }; static const ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }; int rc; ble_gap_test_util_init(); /* Start a discovery procedure. */ rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, &disc_params, ble_gap_test_util_disc_cb, NULL, -1, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure host indicates BLE_HS_EBUSY if we try to connect. */ rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, BLE_HS_FOREVER, &conn_params, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(rc == BLE_HS_EBUSY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_gen_fail_evt) { static const ble_addr_t peer_addr = {BLE_ADDR_PUBLIC, {1, 2, 3, 4, 5, 6}}; struct ble_gap_conn_complete evt; struct ble_hci_ev_disconn_cmp disc_evt; int rc; ble_gap_test_util_init(); /* Start a connect procedure. */ rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &peer_addr, 0, NULL, ble_gap_test_util_copy_cb, NULL, 0); TEST_ASSERT_FATAL(rc == 0); /* Controller indicates failure via connect complete event. */ memset(&evt, 0, sizeof evt); evt.status = BLE_ERR_SUCCESS; evt.connection_handle = 6; evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER; evt.peer_addr_type = BLE_ADDR_PUBLIC; memcpy(evt.peer_addr, peer_addr.val, 6); rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure failed connect was reported to application. */ TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_event.connect.status == BLE_HS_HCI_ERR(BLE_ERR_SUCCESS)); memset(&disc_evt, 0, sizeof disc_evt); disc_evt.conn_handle = htole16(6); disc_evt.status = BLE_ERR_SUCCESS; disc_evt.reason = BLE_ERR_CONN_ESTABLISHMENT; ble_gap_rx_disconn_complete(&disc_evt); /* Ensure failed connect was reported to application. */ TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_DISCONNECT); TEST_ASSERT(ble_gap_test_event.disconnect.reason == BLE_HS_HCI_ERR(BLE_ERR_CONN_ESTABLISHMENT)); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_conn_gen) { ble_gap_test_case_conn_gen_good(); ble_gap_test_case_conn_gen_bad_args(); ble_gap_test_case_conn_gen_dflt_params(); ble_gap_test_case_conn_gen_already(); ble_gap_test_case_conn_gen_done(); ble_gap_test_case_conn_gen_busy(); ble_gap_test_case_conn_gen_fail_evt(); } /***************************************************************************** * $cancel * *****************************************************************************/ static void ble_gap_test_util_conn_cancel(uint8_t hci_status) { struct ble_gap_conn_complete evt; int rc; /* Initiate cancel procedure. */ rc = ble_hs_test_util_conn_cancel(hci_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status)); /* Verify tx of cancel create connection command. */ ble_hs_test_util_hci_verify_tx_create_conn_cancel(); if (rc != 0) { return; } TEST_ASSERT(ble_gap_master_in_progress()); /* Receive connection complete event. */ memset(&evt, 0, sizeof evt); evt.status = BLE_ERR_UNK_CONN_ID; /* test if host correctly ignores other fields if status is error */ evt.connection_handle = 0x0fff; rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_event.connect.status == BLE_HS_EAPP); } static void ble_gap_test_util_conn_and_cancel(uint8_t *peer_addr, uint8_t hci_status) { ble_addr_t addr = { BLE_ADDR_PUBLIC }; int rc; ble_gap_test_util_init(); memcpy(addr.val, peer_addr, 6); /* Begin creating a connection. */ rc = ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &addr, 0, NULL, ble_gap_test_util_connect_cb, NULL, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_master_in_progress()); /* Initiate cancel procedure. */ ble_gap_test_util_conn_cancel(hci_status); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == BLE_HS_ENOTCONN); } TEST_CASE_SELF(ble_gap_test_case_conn_cancel_bad_args) { int rc; ble_gap_test_util_init(); /* Initiate cancel procedure with no connection in progress. */ TEST_ASSERT(!ble_gap_master_in_progress()); rc = ble_hs_test_util_conn_cancel(0); TEST_ASSERT(rc == BLE_HS_EALREADY); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_cancel_good) { uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_conn_and_cancel(peer_addr, 0); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_event.connect.status == BLE_HS_EAPP); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == BLE_HS_CONN_HANDLE_NONE); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_cancel_ctlr_fail) { struct ble_gap_conn_complete evt; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_conn_and_cancel(peer_addr, BLE_ERR_REPEATED_ATTEMPTS); /* Make sure the host didn't invoke the application callback. The cancel * failure was indicated via the return code from the gap call. */ TEST_ASSERT(ble_gap_test_event.type == 0xff); /* ble_gap_rx_conn_complete() will send extra HCI command, need phony * ack */ ble_hs_test_util_hci_ack_set(ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_REM_FEAT), 0); /* Allow connection complete to succeed. */ memset(&evt, 0, sizeof evt); evt.status = BLE_ERR_SUCCESS; evt.connection_handle = 2; evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER; memcpy(evt.peer_addr, peer_addr, 6); rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_conn_cancel) { ble_gap_test_case_conn_cancel_good(); ble_gap_test_case_conn_cancel_bad_args(); ble_gap_test_case_conn_cancel_ctlr_fail(); } /***************************************************************************** * $terminate * *****************************************************************************/ static void ble_gap_test_util_terminate(uint8_t *peer_addr, uint8_t hci_status) { struct ble_hci_ev_disconn_cmp evt; int rc; ble_gap_test_util_init(); /* Create a connection. */ ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); /* Reset the callback event code; we don't care about the successful * connection in this test. */ ble_gap_test_event.type = -1; /* Terminate the connection. */ rc = ble_hs_test_util_conn_terminate(2, hci_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status)); TEST_ASSERT(!ble_gap_master_in_progress()); /* Verify tx of disconnect command. */ ble_gap_test_util_verify_tx_disconnect(); if (hci_status == 0) { /* Receive disconnection complete event. */ evt.conn_handle = htole16(2); evt.status = 0; evt.reason = BLE_ERR_CONN_TERM_LOCAL; ble_gap_rx_disconn_complete(&evt); } } TEST_CASE_SELF(ble_gap_test_case_conn_terminate_bad_args) { int rc; ble_gap_test_util_init(); /*** Nonexistent connection. */ rc = ble_hs_test_util_conn_terminate(2, 0); TEST_ASSERT(rc == BLE_HS_ENOTCONN); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_terminate_good) { uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_terminate(peer_addr, 0); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_DISCONNECT); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(BLE_ERR_CONN_TERM_LOCAL)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(ble_gap_test_conn_desc.peer_id_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_arg == NULL); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == BLE_HS_ENOTCONN); TEST_ASSERT(!ble_gap_master_in_progress()); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_terminate_ctlr_fail) { struct ble_hci_ev_disconn_cmp evt; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); /* Create a connection. */ ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); /* Terminate the connection. */ rc = ble_hs_test_util_conn_terminate(2, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(!ble_gap_master_in_progress()); /* Verify tx of disconnect command. */ ble_gap_test_util_verify_tx_disconnect(); /* Receive failed disconnection complete event. */ evt.conn_handle = htole16(2); evt.status = BLE_ERR_UNSUPPORTED; evt.reason = 0; ble_gap_rx_disconn_complete(&evt); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_TERM_FAILURE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(BLE_ERR_UNSUPPORTED)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(ble_gap_test_conn_desc.peer_id_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_arg == NULL); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == 0); TEST_ASSERT(!ble_gap_master_in_progress()); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_terminate_hci_fail) { uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_terminate(peer_addr, BLE_ERR_REPEATED_ATTEMPTS); TEST_ASSERT(ble_gap_test_event.type == 0xff); TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == 0); TEST_ASSERT(!ble_gap_master_in_progress()); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_conn_terminate) { ble_gap_test_case_conn_terminate_bad_args(); ble_gap_test_case_conn_terminate_good(); ble_gap_test_case_conn_terminate_ctlr_fail(); ble_gap_test_case_conn_terminate_hci_fail(); } /***************************************************************************** * $conn find * *****************************************************************************/ TEST_CASE_SELF(ble_gap_test_case_conn_find) { struct ble_gap_conn_desc desc; struct ble_hs_conn *conn; uint8_t pub_addr[6]; int rc; /*** We are master; public addresses. */ ble_gap_test_util_init(); rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, pub_addr, NULL); TEST_ASSERT_FATAL(rc == 0); ble_hs_test_util_create_rpa_conn(8, BLE_OWN_ADDR_PUBLIC, ((uint8_t[6]){0,0,0,0,0,0}), BLE_ADDR_PUBLIC, ((uint8_t[6]){2,3,4,5,6,7}), ((uint8_t[6]){0,0,0,0,0,0}), BLE_HS_TEST_CONN_FEAT_ALL, ble_gap_test_util_connect_cb, NULL); rc = ble_gap_conn_find(8, &desc); TEST_ASSERT_FATAL(rc == 0); TEST_ASSERT(desc.conn_handle == 8); TEST_ASSERT(desc.our_id_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(desc.our_ota_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(desc.peer_ota_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(desc.role == BLE_GAP_ROLE_MASTER); TEST_ASSERT(memcmp(desc.our_ota_addr.val, pub_addr, 6) == 0); TEST_ASSERT(memcmp(desc.our_id_addr.val, pub_addr, 6) == 0); TEST_ASSERT(memcmp(desc.peer_ota_addr.val, ((uint8_t[6]){2,3,4,5,6,7}), 6) == 0); TEST_ASSERT(memcmp(desc.peer_id_addr.val, ((uint8_t[6]){2,3,4,5,6,7}), 6) == 0); TEST_ASSERT(desc.conn_itvl == BLE_GAP_INITIAL_CONN_ITVL_MAX); TEST_ASSERT(desc.conn_latency == BLE_GAP_INITIAL_CONN_LATENCY); TEST_ASSERT(desc.supervision_timeout == BLE_GAP_INITIAL_SUPERVISION_TIMEOUT); TEST_ASSERT(desc.master_clock_accuracy == 0); TEST_ASSERT(!desc.sec_state.encrypted); TEST_ASSERT(!desc.sec_state.authenticated); TEST_ASSERT(!desc.sec_state.bonded); /*** Swap roles. */ ble_hs_lock(); conn = ble_hs_conn_find(8); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); rc = ble_gap_conn_find(8, &desc); TEST_ASSERT_FATAL(rc == 0); TEST_ASSERT(desc.role == BLE_GAP_ROLE_SLAVE); /*** We are master; RPAs. */ ble_gap_test_util_init(); rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, pub_addr, NULL); TEST_ASSERT_FATAL(rc == 0); ble_hs_test_util_create_rpa_conn(54, BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT, ((uint8_t[6]){0x40,1,2,3,4,5}), BLE_ADDR_RANDOM_ID, ((uint8_t[6]){3,4,5,6,7,8}), ((uint8_t[6]){0x50,1,2,3,4,5}), BLE_HS_TEST_CONN_FEAT_ALL, ble_gap_test_util_connect_cb, NULL); rc = ble_gap_conn_find(54, &desc); TEST_ASSERT_FATAL(rc == 0); TEST_ASSERT(desc.conn_handle == 54); TEST_ASSERT(desc.our_id_addr.type == BLE_ADDR_PUBLIC); TEST_ASSERT(desc.our_ota_addr.type == BLE_ADDR_RANDOM); TEST_ASSERT(desc.peer_ota_addr.type == BLE_ADDR_RANDOM); TEST_ASSERT(desc.role == BLE_GAP_ROLE_MASTER); TEST_ASSERT(memcmp(desc.our_ota_addr.val, ((uint8_t[6]){0x40,1,2,3,4,5}), 6) == 0); TEST_ASSERT(memcmp(desc.our_id_addr.val, pub_addr, 6) == 0); TEST_ASSERT(memcmp(desc.peer_ota_addr.val, ((uint8_t[6]){0x50,1,2,3,4,5}), 6) == 0); TEST_ASSERT(memcmp(desc.peer_id_addr.val, ((uint8_t[6]){3,4,5,6,7,8}), 6) == 0); TEST_ASSERT(desc.conn_itvl == BLE_GAP_INITIAL_CONN_ITVL_MAX); TEST_ASSERT(desc.conn_latency == BLE_GAP_INITIAL_CONN_LATENCY); TEST_ASSERT(desc.supervision_timeout == BLE_GAP_INITIAL_SUPERVISION_TIMEOUT); TEST_ASSERT(desc.master_clock_accuracy == 0); TEST_ASSERT(!desc.sec_state.encrypted); TEST_ASSERT(!desc.sec_state.authenticated); TEST_ASSERT(!desc.sec_state.bonded); /*** Swap roles. */ ble_hs_lock(); conn = ble_hs_conn_find(54); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); rc = ble_gap_conn_find(54, &desc); TEST_ASSERT_FATAL(rc == 0); TEST_ASSERT(desc.role == BLE_GAP_ROLE_SLAVE); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_conn_find) { ble_gap_test_case_conn_find(); } /***************************************************************************** * $advertise * *****************************************************************************/ static void ble_gap_test_util_adv(uint8_t own_addr_type, const ble_addr_t *peer_addr, uint8_t conn_mode, uint8_t disc_mode, int connect_status, int cmd_fail_idx, uint8_t fail_status) { struct ble_gap_conn_complete evt; struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields adv_fields; uint8_t hci_status; int cmd_idx; int rc; ble_gap_test_util_init(); adv_params = ble_hs_test_util_adv_params; adv_params.conn_mode = conn_mode; adv_params.disc_mode = disc_mode; TEST_ASSERT(!ble_gap_adv_active()); cmd_idx = 0; memset(&adv_fields, 0, sizeof adv_fields); adv_fields.tx_pwr_lvl_is_present = 1; adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; rc = ble_hs_test_util_adv_set_fields(&adv_fields, cmd_fail_idx, fail_status); if (cmd_fail_idx < 2) { hci_status = fail_status; } else { hci_status = 0; } TEST_ASSERT_FATAL(rc == BLE_HS_HCI_ERR(hci_status)); cmd_idx += 2; if (rc == 0) { ble_gap_test_util_verify_tx_adv_data(); } if (fail_status == 0 || cmd_fail_idx >= cmd_idx) { rc = ble_hs_test_util_adv_start(own_addr_type, peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, cmd_fail_idx - cmd_idx, fail_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(fail_status)); cmd_idx++; } if (fail_status == 0 || cmd_fail_idx >= cmd_idx) { /* Verify tx of set advertising params command. */ ble_gap_test_util_verify_tx_adv_params(); } cmd_idx++; if (fail_status == 0 || cmd_fail_idx >= cmd_idx) { /* Verify tx of set advertise enable command. */ ble_gap_test_util_verify_tx_adv_enable(1); } cmd_idx++; if (connect_status != -1 && (fail_status == 0 || cmd_fail_idx >= cmd_idx)) { TEST_ASSERT(ble_gap_adv_active()); /* Receive a connection complete event. */ if (conn_mode != BLE_GAP_CONN_MODE_NON) { if (connect_status == BLE_ERR_SUCCESS) { /* * ble_gap_rx_conn_complete() will send extra HCI command, need * phony ack */ ble_hs_test_util_hci_ack_set( ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_REM_FEAT), 0); } memset(&evt, 0, sizeof evt); evt.status = connect_status; if (connect_status == BLE_ERR_SUCCESS) { evt.connection_handle = 2; evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_SLAVE; memcpy(evt.peer_addr, peer_addr->val, 6); } else { /* test if host correctly ignores other fields if status is * error */ evt.connection_handle = 0x0fff; } rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT(rc == 0); if (connect_status == 0 || connect_status == BLE_ERR_DIR_ADV_TMO) { TEST_ASSERT(!ble_gap_adv_active()); } else { TEST_ASSERT(ble_gap_adv_active()); } } } } TEST_CASE_SELF(ble_gap_test_case_adv_bad_args) { struct ble_gap_adv_params adv_params; ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; ble_addr_t peer_addr_inv = { 12, { 1, 2, 3, 4, 5, 6 }}; int rc; ble_gap_test_util_init(); TEST_ASSERT(!ble_gap_adv_active()); /*** Invalid discoverable mode. */ adv_params = ble_hs_test_util_adv_params; adv_params.disc_mode = 43; rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, &peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT(rc == BLE_HS_EINVAL); TEST_ASSERT(!ble_gap_adv_active()); /*** Invalid connectable mode. */ adv_params = ble_hs_test_util_adv_params; adv_params.conn_mode = 27; rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, &peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT(rc == BLE_HS_EINVAL); TEST_ASSERT(!ble_gap_adv_active()); /*** Invalid peer address type with directed advertisable mode. */ adv_params = ble_hs_test_util_adv_params; adv_params.conn_mode = BLE_GAP_CONN_MODE_DIR; rc = ble_hs_test_util_adv_start( BLE_OWN_ADDR_PUBLIC, &peer_addr_inv, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT(rc == BLE_HS_EINVAL); TEST_ASSERT(!ble_gap_adv_active()); /*** Advertising already in progress. */ adv_params = ble_hs_test_util_adv_params; rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, &peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_adv_active()); rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, &peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT(rc == BLE_HS_EALREADY); TEST_ASSERT(ble_gap_adv_active()); ble_hs_test_util_assert_mbufs_freed(NULL); } static void ble_gap_test_util_adv_verify_dflt_params(uint8_t own_addr_type, const ble_addr_t *peer_addr, uint8_t conn_mode, uint8_t disc_mode) { struct ble_hci_le_set_adv_params_cp hci_cmd; struct ble_gap_adv_params adv_params; uint8_t *hci_buf; uint8_t hci_param_len; int rc; ble_gap_test_util_init(); TEST_ASSERT(!ble_gap_adv_active()); adv_params = ble_hs_test_util_adv_params; adv_params.conn_mode = conn_mode; adv_params.disc_mode = disc_mode; /* Let stack calculate all default parameters. */ adv_params.itvl_min = 0; adv_params.itvl_max = 0; adv_params.channel_map = 0; adv_params.filter_policy = 0; adv_params.high_duty_cycle = 0; rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC, peer_addr, &adv_params, BLE_HS_FOREVER, ble_gap_test_util_connect_cb, NULL, 0, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure default parameters properly filled in. */ hci_buf = ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_PARAMS, &hci_param_len); TEST_ASSERT_FATAL(hci_buf != NULL); TEST_ASSERT_FATAL(hci_param_len == BLE_HCI_SET_ADV_PARAM_LEN); hci_cmd.min_interval = get_le16(hci_buf + 0); hci_cmd.max_interval = get_le16(hci_buf + 2); hci_cmd.type = hci_buf[4]; hci_cmd.own_addr_type = hci_buf[5]; hci_cmd.peer_addr_type = hci_buf[6]; memcpy(hci_cmd.peer_addr, hci_buf + 7, 6); hci_cmd.chan_map = hci_buf[13]; hci_cmd.filter_policy = hci_buf[14]; if (conn_mode == BLE_GAP_CONN_MODE_NON) { TEST_ASSERT(hci_cmd.min_interval == BLE_GAP_ADV_FAST_INTERVAL2_MIN); TEST_ASSERT(hci_cmd.max_interval == BLE_GAP_ADV_FAST_INTERVAL2_MAX); } else { TEST_ASSERT(hci_cmd.min_interval == BLE_GAP_ADV_FAST_INTERVAL1_MIN); TEST_ASSERT(hci_cmd.max_interval == BLE_GAP_ADV_FAST_INTERVAL1_MAX); } if (conn_mode == BLE_GAP_CONN_MODE_NON) { if (disc_mode == BLE_GAP_DISC_MODE_NON) { TEST_ASSERT(hci_cmd.type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND); } else { TEST_ASSERT(hci_cmd.type == BLE_HCI_ADV_TYPE_ADV_SCAN_IND); } } else if (conn_mode == BLE_GAP_CONN_MODE_UND) { TEST_ASSERT(hci_cmd.type == BLE_HCI_ADV_TYPE_ADV_IND); } else { TEST_ASSERT(hci_cmd.type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD); } } TEST_CASE_SELF(ble_gap_test_case_adv_dflt_params) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int d; int c; for (c = BLE_GAP_CONN_MODE_NON; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { ble_gap_test_util_adv_verify_dflt_params( BLE_OWN_ADDR_PUBLIC, &peer_addr, c, d); } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_adv_good) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int d; int c; for (c = BLE_GAP_CONN_MODE_NON; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { ble_gap_test_util_adv(BLE_OWN_ADDR_PUBLIC, &peer_addr, c, d, BLE_ERR_SUCCESS, -1, 0); if (c != BLE_GAP_CONN_MODE_NON) { TEST_ASSERT(!ble_gap_adv_active()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_conn_status == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr.val, 6) == 0); TEST_ASSERT(ble_gap_test_conn_arg == NULL); } } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_adv_ctlr_fail) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int d; int c; for (c = BLE_GAP_CONN_MODE_NON + 1; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { ble_gap_test_util_adv(BLE_OWN_ADDR_PUBLIC, &peer_addr, c, d, BLE_ERR_DIR_ADV_TMO, -1, 0); TEST_ASSERT(!ble_gap_adv_active()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_ADV_COMPLETE); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == BLE_HS_CONN_HANDLE_NONE); TEST_ASSERT(ble_gap_test_conn_arg == NULL); } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_adv_hci_fail) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int fail_idx; int d; int c; for (c = BLE_GAP_CONN_MODE_NON; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { for (fail_idx = 0; fail_idx < 4; fail_idx++) { ble_gap_test_util_adv(BLE_OWN_ADDR_PUBLIC, &peer_addr, c, d, 0, fail_idx, BLE_ERR_UNSUPPORTED); TEST_ASSERT(!ble_gap_adv_active()); TEST_ASSERT(ble_gap_test_event.type == 0xff); } } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_adv) { ble_gap_test_case_adv_bad_args(); ble_gap_test_case_adv_dflt_params(); ble_gap_test_case_adv_good(); ble_gap_test_case_adv_ctlr_fail(); ble_gap_test_case_adv_hci_fail(); } /***************************************************************************** * $stop advertise * *****************************************************************************/ static void ble_gap_test_util_stop_adv(const ble_addr_t *peer_addr, uint8_t conn_mode, uint8_t disc_mode, int cmd_fail_idx, uint8_t fail_status) { uint8_t hci_status; int rc; ble_gap_test_util_init(); /* Start advertising; don't rx a successful connection event. */ ble_gap_test_util_adv(BLE_OWN_ADDR_PUBLIC, peer_addr, conn_mode, disc_mode, -1, -1, 0); TEST_ASSERT(ble_gap_adv_active()); /* Stop advertising. */ hci_status = cmd_fail_idx == 0 ? fail_status : 0; rc = ble_hs_test_util_adv_stop(hci_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status)); /* Verify tx of advertising enable command. */ ble_gap_test_util_verify_tx_adv_enable(0); } TEST_CASE_SELF(ble_gap_test_case_stop_adv_good) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int d; int c; for (c = BLE_GAP_CONN_MODE_NON; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { ble_gap_test_util_stop_adv(&peer_addr, c, d, -1, 0); TEST_ASSERT(!ble_gap_adv_active()); TEST_ASSERT(ble_gap_test_event.type == 0xff); TEST_ASSERT(ble_gap_test_conn_status == -1); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == (uint16_t)-1); TEST_ASSERT(ble_gap_test_conn_arg == (void *)-1); } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_stop_adv_hci_fail) { ble_addr_t peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; int d; int c; for (c = BLE_GAP_CONN_MODE_NON; c < BLE_GAP_CONN_MODE_MAX; c++) { for (d = BLE_GAP_DISC_MODE_NON; d < BLE_GAP_DISC_MODE_MAX; d++) { ble_gap_test_util_stop_adv(&peer_addr, c, d, 0, BLE_ERR_UNSUPPORTED); TEST_ASSERT(ble_gap_adv_active()); TEST_ASSERT(ble_gap_test_event.type == 0xff); TEST_ASSERT(ble_gap_test_conn_status == -1); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == (uint16_t)-1); TEST_ASSERT(ble_gap_test_conn_arg == (void *)-1); } } ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_stop_adv) { ble_gap_test_case_stop_adv_good(); ble_gap_test_case_stop_adv_hci_fail(); } /***************************************************************************** * $update connection * *****************************************************************************/ static void ble_gap_test_util_update_verify_params(struct ble_gap_upd_params *params, uint8_t ble_hs_err) { int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); rc = ble_hs_test_util_conn_update(2, params, 0); TEST_ASSERT(rc == ble_hs_err); } static void ble_gap_test_util_update_no_l2cap(struct ble_gap_upd_params *params, int master, uint8_t hci_status, int event_status) { struct ble_hs_conn *conn; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); if (!master) { ble_hs_lock(); conn = ble_hs_conn_find(2); TEST_ASSERT_FATAL(conn != NULL); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); } /* Erase callback info reported during connection establishment; we only * care about updates. */ ble_gap_test_util_reset_cb_info(); TEST_ASSERT(!ble_gap_master_in_progress()); rc = ble_hs_test_util_conn_update(2, params, hci_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status)); TEST_ASSERT(!ble_gap_master_in_progress()); /* Verify tx of connection update command. */ ble_gap_test_util_verify_tx_update_conn(params); if (rc == 0) { TEST_ASSERT(ble_gap_dbg_update_active(2)); /* Attempt two duplicate updates; ensure BLE_HS_EALREADY gets returned * both times. Make sure initial update still completes successfully * (MYNEWT-702). */ rc = ble_hs_test_util_conn_update(2, params, 0); TEST_ASSERT(rc == BLE_HS_EALREADY); rc = ble_hs_test_util_conn_update(2, params, 0); TEST_ASSERT(rc == BLE_HS_EALREADY); /* Receive connection update complete event. */ ble_gap_test_util_rx_update_complete(event_status, params); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(event_status)); if (event_status == 0) { TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == params->itvl_max); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == params->latency); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == params->supervision_timeout); } } else { TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); TEST_ASSERT(ble_gap_test_event.type == 0xff); } } static void ble_gap_test_util_update_l2cap(struct ble_gap_upd_params *params, uint16_t l2cap_result) { struct ble_l2cap_sig_update_params l2cap_params; struct ble_hs_conn *conn; uint8_t id; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn_feat(2, peer_addr, BLE_HS_TEST_CONN_FEAT_NO_CONN_PARAM, ble_gap_test_util_connect_cb, NULL); ble_hs_lock(); conn = ble_hs_conn_find(2); TEST_ASSERT_FATAL(conn != NULL); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); /* Erase callback info reported during connection establishment; we only * care about updates. */ ble_gap_test_util_reset_cb_info(); rc = ble_hs_test_util_conn_update(2, params, 0xFF); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_dbg_update_active(2)); l2cap_params.itvl_min = params->itvl_min; l2cap_params.itvl_max = params->itvl_max; l2cap_params.slave_latency = params->latency; l2cap_params.timeout_multiplier = params->supervision_timeout; id = ble_hs_test_util_verify_tx_l2cap_update_req(&l2cap_params); /* Receive l2cap connection parameter update response. */ ble_hs_test_util_rx_l2cap_update_rsp(2, id, l2cap_result); TEST_ASSERT(!ble_gap_dbg_update_active(2)); if (l2cap_result == BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT) { /* Receive connection update complete event. */ ble_gap_test_util_rx_update_complete(0, params); } TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); if (l2cap_result != BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT) { TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_EREJECT); } else { TEST_ASSERT(ble_gap_test_conn_status == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == params->itvl_max); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == params->latency); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == params->supervision_timeout); } TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); } static void ble_gap_test_util_update_no_l2cap_tmo(struct ble_gap_upd_params *params, int master) { struct ble_hs_conn *conn; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); if (!master) { ble_hs_lock(); conn = ble_hs_conn_find(2); TEST_ASSERT_FATAL(conn != NULL); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); } /* Erase callback info reported during connection establishment; we only * care about updates. */ ble_gap_test_util_reset_cb_info(); TEST_ASSERT(!ble_gap_master_in_progress()); rc = ble_hs_test_util_conn_update(2, params, 0); TEST_ASSERT(rc == 0); TEST_ASSERT(!ble_gap_master_in_progress()); /* Verify tx of connection update command. */ ble_gap_test_util_verify_tx_update_conn(params); /* Ensure no update event reported. */ TEST_ASSERT(ble_gap_test_event.type == 0xff); /* Advance 39 seconds; ensure no timeout reported. */ os_time_advance(39 * OS_TICKS_PER_SEC); ble_gap_timer(); TEST_ASSERT(ble_gap_test_event.type == 0xff); /* Advance 40th second; ensure timeout reported. */ os_time_advance(1 * OS_TICKS_PER_SEC); /* Timeout will result in a terminate HCI command being sent; schedule ack * from controller. */ ble_hs_test_util_hci_ack_set_disconnect(0); ble_gap_timer(); /* Verify terminate was sent. */ ble_gap_test_util_verify_tx_disconnect(); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_ETIMEOUT); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); } static void ble_gap_test_util_update_l2cap_tmo(struct ble_gap_upd_params *params, uint8_t hci_status, uint8_t event_status, int rx_l2cap) { struct ble_l2cap_sig_update_params l2cap_params; struct ble_hs_conn *conn; uint8_t id; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn_feat(2, peer_addr, BLE_HS_TEST_CONN_FEAT_NO_CONN_PARAM, ble_gap_test_util_connect_cb, NULL); ble_hs_lock(); conn = ble_hs_conn_find(2); TEST_ASSERT_FATAL(conn != NULL); conn->bhc_flags &= ~BLE_HS_CONN_F_MASTER; ble_hs_unlock(); /* Erase callback info reported during connection establishment; we only * care about updates. */ ble_gap_test_util_reset_cb_info(); rc = ble_hs_test_util_conn_update(2, params, 0xFF); TEST_ASSERT(rc == 0); TEST_ASSERT(ble_gap_dbg_update_active(2)); if (rx_l2cap) { l2cap_params.itvl_min = params->itvl_min; l2cap_params.itvl_max = params->itvl_max; l2cap_params.slave_latency = params->latency; l2cap_params.timeout_multiplier = params->supervision_timeout; id = ble_hs_test_util_verify_tx_l2cap_update_req(&l2cap_params); /* Receive l2cap connection parameter update response. */ ble_hs_test_util_rx_l2cap_update_rsp( 2, id, BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT); TEST_ASSERT(!ble_gap_dbg_update_active(2)); } else { TEST_ASSERT(ble_gap_dbg_update_active(2)); } /* Ensure no update event reported. */ TEST_ASSERT(ble_gap_test_event.type == 0xff); /* Advance 29 seconds; ensure no timeout reported. * Note: L2CAP signaling timeout is 30 sec, GAP update timeout is 40 sec */ os_time_advance(29 * OS_TICKS_PER_SEC); ble_gap_timer(); ble_l2cap_sig_timer(); TEST_ASSERT(ble_gap_test_event.type == 0xff); /* Advance 30th second; ensure timeout reported. */ os_time_advance(1 * OS_TICKS_PER_SEC); /* If L2CAP response has been received, GAP Timer is removed */ if (!rx_l2cap) { /* Timeout will result in a terminate HCI command being sent; schedule ack * from controller. */ ble_hs_test_util_hci_ack_set_disconnect(0); ble_gap_timer(); ble_l2cap_sig_timer(); /* Verify terminate was sent. */ ble_gap_test_util_verify_tx_disconnect(); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_ETIMEOUT); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); } else { ble_gap_timer(); ble_l2cap_sig_timer(); TEST_ASSERT(ble_gap_test_event.type == 0xff); } } static void ble_gap_test_util_update_peer(uint8_t status, struct ble_gap_upd_params *params) { uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(!ble_gap_master_in_progress()); /* Receive connection update complete event. */ ble_gap_test_util_rx_update_complete(status, params); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(status)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); if (status == 0) { TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == params->itvl_max); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == params->latency); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == params->supervision_timeout); } TEST_ASSERT(!ble_gap_dbg_update_active(2)); } static void ble_gap_test_util_update_req_pos(struct ble_gap_upd_params *peer_params, struct ble_gap_upd_params *self_params, int cmd_fail_idx, uint8_t hci_status) { int cmd_idx; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); cmd_idx = 0; ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(!ble_gap_master_in_progress()); ble_gap_test_conn_self_params = *self_params; rc = ble_gap_test_util_rx_param_req(peer_params, 1, &cmd_idx, cmd_fail_idx, hci_status); if (rc != 0) { goto hci_fail; } TEST_ASSERT(!ble_gap_master_in_progress()); /* We don't maintain an update entry when the peer initiates. */ TEST_ASSERT(!ble_gap_dbg_update_active(2)); /* Verify tx of connection parameters reply command. */ ble_gap_test_util_verify_tx_params_reply_pos(); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); /* Receive connection update complete event. */ ble_gap_test_util_rx_update_complete(0, self_params); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == self_params->itvl_max); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == self_params->latency); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == self_params->supervision_timeout); return; hci_fail: TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(hci_status)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == BLE_GAP_INITIAL_CONN_ITVL_MAX); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == BLE_GAP_INITIAL_CONN_LATENCY); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == BLE_GAP_INITIAL_SUPERVISION_TIMEOUT); } static void ble_gap_test_util_update_req_neg(struct ble_gap_upd_params *peer_params, int cmd_fail_idx, uint8_t hci_status) { int cmd_idx; int reason; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); cmd_idx = 0; reason = BLE_ERR_UNSPECIFIED; ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, &reason); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); rc = ble_gap_test_util_rx_param_req(peer_params, 0, &cmd_idx, cmd_fail_idx, hci_status); if (rc != 0) { goto hci_fail; } TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); /* Verify tx of connection parameters negative reply command. */ ble_gap_test_util_verify_tx_params_reply_neg(reason); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); return; hci_fail: TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(hci_status)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == BLE_GAP_INITIAL_CONN_ITVL_MAX); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == BLE_GAP_INITIAL_CONN_LATENCY); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == BLE_GAP_INITIAL_SUPERVISION_TIMEOUT); } static void ble_gap_test_util_update_req_concurrent( struct ble_gap_upd_params *init_params, struct ble_gap_upd_params *peer_params, struct ble_gap_upd_params *self_params, int cmd_fail_idx, uint8_t fail_status) { uint8_t hci_status; int cmd_idx; int rc; uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); hci_status = cmd_fail_idx == 0 ? fail_status : 0; rc = ble_hs_test_util_conn_update(2, init_params, hci_status); TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status)); TEST_ASSERT(!ble_gap_master_in_progress()); /* Verify tx of connection update command. */ ble_gap_test_util_verify_tx_update_conn(init_params); if (rc == 0) { TEST_ASSERT(ble_gap_dbg_update_active(2)); } else { TEST_ASSERT(!ble_gap_dbg_update_active(2)); return; } TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_dbg_update_active(2)); /* Receive connection parameter update request from peer. */ ble_gap_test_conn_self_params = *self_params; rc = ble_gap_test_util_rx_param_req(peer_params, 1, &cmd_idx, cmd_fail_idx, hci_status); if (rc != 0) { goto hci_fail; } TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_dbg_update_active(2)); /* Verify tx of connection parameters reply command. */ ble_gap_test_util_verify_tx_params_reply_pos(); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(ble_gap_dbg_update_active(2)); /* Receive connection update complete event. */ ble_gap_test_util_rx_update_complete(0, self_params); TEST_ASSERT(!ble_gap_master_in_progress()); TEST_ASSERT(!ble_gap_dbg_update_active(2)); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == self_params->itvl_max); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == self_params->latency); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == self_params->supervision_timeout); return; hci_fail: TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONN_UPDATE); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_HCI_ERR(fail_status)); TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == 2); TEST_ASSERT(memcmp(ble_gap_test_conn_desc.peer_id_addr.val, peer_addr, 6) == 0); TEST_ASSERT(ble_gap_test_conn_desc.conn_itvl == BLE_GAP_INITIAL_CONN_ITVL_MAX); TEST_ASSERT(ble_gap_test_conn_desc.conn_latency == BLE_GAP_INITIAL_CONN_LATENCY); TEST_ASSERT(ble_gap_test_conn_desc.supervision_timeout == BLE_GAP_INITIAL_SUPERVISION_TIMEOUT); } TEST_CASE_SELF(ble_gap_test_case_update_conn_good) { ble_gap_test_util_update_no_l2cap( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), 1, 0, 0); ble_gap_test_util_update_no_l2cap( ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), 1, 0, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_conn_verify_params) { /* GOOD */ ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), 0); /* BAD */ ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 1, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), BLE_HS_EINVAL); ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 0x0C80 + 1, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), BLE_HS_EINVAL); ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 50, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), BLE_HS_EINVAL); ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 100, .supervision_timeout = 200, .latency = 0x01F4, .min_ce_len = 554, .max_ce_len = 554, }}), BLE_HS_EINVAL); ble_gap_test_util_update_verify_params( ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 600, .supervision_timeout = 300, .latency = 1, .min_ce_len = 554, .max_ce_len = 554, }}), BLE_HS_EINVAL); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_conn_bad) { ble_gap_test_util_update_no_l2cap( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), 1, 0, BLE_ERR_LMP_COLLISION); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_conn_hci_fail) { ble_gap_test_util_update_no_l2cap( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), 1, BLE_ERR_UNSUPPORTED, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_conn_l2cap) { struct ble_gap_upd_params params = { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }; /* Accepted L2CAP. */ ble_gap_test_util_update_l2cap(¶ms, BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT); /* Rejected L2CAP. */ ble_gap_test_util_update_l2cap(¶ms, BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_peer_good) { ble_gap_test_util_update_peer(0, ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 0, .min_ce_len = 123, .max_ce_len = 456, }})); ble_gap_test_util_update_peer(0, ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 100, .supervision_timeout = 100, .min_ce_len = 554, .max_ce_len = 554, }})); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_req_good) { ble_gap_test_util_update_req_pos( ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), -1, 0); ble_gap_test_util_update_req_pos( ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 100, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 554, .max_ce_len = 554, }}), -1, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_req_hci_fail) { ble_gap_test_util_update_req_pos( ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), 0, BLE_ERR_UNSUPPORTED); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_req_reject) { ble_gap_test_util_update_req_neg( ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), -1, 0); ble_gap_test_util_update_req_neg( ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), -1, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_concurrent_good) { ble_gap_test_util_update_req_concurrent( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), -1, 0); ble_gap_test_util_update_req_concurrent( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 20, .itvl_max = 200, .supervision_timeout = 350, .min_ce_len = 111, .max_ce_len = 222, }}), -1, 0); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_update_concurrent_hci_fail) { ble_gap_test_util_update_req_concurrent( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 20, .itvl_max = 200, .supervision_timeout = 350, .min_ce_len = 111, .max_ce_len = 222, }}), 0, BLE_ERR_UNSUPPORTED); ble_gap_test_util_update_req_concurrent( ((struct ble_gap_upd_params[]) { { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 50, .itvl_max = 500, .supervision_timeout = 800, .min_ce_len = 555, .max_ce_len = 888, }}), ((struct ble_gap_upd_params[]) { { .itvl_min = 20, .itvl_max = 200, .supervision_timeout = 350, .min_ce_len = 111, .max_ce_len = 222, }}), 1, BLE_ERR_UNSUPPORTED); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_update_conn) { ble_gap_test_case_update_conn_good(); ble_gap_test_case_update_conn_bad(); ble_gap_test_case_update_conn_hci_fail(); ble_gap_test_case_update_conn_l2cap(); ble_gap_test_case_update_peer_good(); ble_gap_test_case_update_req_good(); ble_gap_test_case_update_req_hci_fail(); ble_gap_test_case_update_req_reject(); ble_gap_test_case_update_concurrent_good(); ble_gap_test_case_update_concurrent_hci_fail(); ble_gap_test_case_update_conn_verify_params(); } /***************************************************************************** * $timeout * *****************************************************************************/ static void ble_gap_test_util_conn_forever(void) { int32_t ticks_from_now; /* Initiate a connect procedure with no timeout. */ ble_hs_test_util_connect( BLE_OWN_ADDR_PUBLIC, &((ble_addr_t) { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}), BLE_HS_FOREVER, NULL, ble_gap_test_util_connect_cb, NULL, 0); /* Ensure no pending GAP event. */ ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); /* Advance 100 seconds; ensure no timeout reported. */ os_time_advance(100 * OS_TICKS_PER_SEC); ble_gap_timer(); TEST_ASSERT(ble_gap_test_event.type == 0xff); TEST_ASSERT(ble_gap_conn_active()); } static void ble_gap_test_util_conn_timeout(int32_t duration_ms) { struct ble_gap_conn_complete evt; uint32_t duration_ticks; int32_t ticks_from_now; int rc; TEST_ASSERT_FATAL(duration_ms != BLE_HS_FOREVER); /* Initiate a connect procedure with the specified timeout. */ ble_hs_test_util_connect( BLE_OWN_ADDR_PUBLIC, &((ble_addr_t) { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}), duration_ms, NULL, ble_gap_test_util_connect_cb, NULL, 0); /* Ensure next GAP event is at the expected time. */ rc = os_time_ms_to_ticks(duration_ms, &duration_ticks); TEST_ASSERT_FATAL(rc == 0); ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == duration_ticks); /* Advance duration ms; ensure timeout event does not get reported before * connection complete event rxed. */ os_time_advance(duration_ms); ble_hs_test_util_hci_ack_set( ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN_CANCEL), 0); TEST_ASSERT(ble_gap_test_event.type == 0xff); ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); /* Ensure cancel create connection command was sent. */ ble_hs_test_util_hci_verify_tx_create_conn_cancel(); /* Ensure timer has been stopped. */ ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); /* Receive the connection complete event indicating a successful cancel. */ memset(&evt, 0, sizeof evt); evt.status = BLE_ERR_UNK_CONN_ID; /* test if host correctly ignores other fields if status is error */ evt.connection_handle = 0x0fff; rc = ble_gap_rx_conn_complete(&evt, 0); TEST_ASSERT_FATAL(rc == 0); /* Ensure the GAP event was triggered. */ TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_CONNECT); TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_ETIMEOUT); /* Clear GAP event for remainder of test. */ ble_gap_test_util_reset_cb_info(); } static void ble_gap_test_util_disc_forever(void) { struct ble_gap_disc_params params; int32_t ticks_from_now; memset(¶ms, 0, sizeof params); /* Initiate a discovery procedure with no timeout. */ ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER, ¶ms, ble_gap_test_util_disc_cb, NULL, -1, 0); /* Ensure no pending GAP event. */ ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); /* Advance 100 seconds; ensure no timeout reported. */ os_time_advance(100 * OS_TICKS_PER_SEC); TEST_ASSERT(ble_gap_test_disc_event_type == -1); TEST_ASSERT(ble_gap_disc_active()); } static void ble_gap_test_util_disc_timeout(int32_t duration_ms) { struct ble_gap_disc_params params; uint32_t duration_ticks; int32_t ticks_from_now; int rc; TEST_ASSERT_FATAL(duration_ms != BLE_HS_FOREVER); memset(¶ms, 0, sizeof params); /* Initiate a discovery procedure with the specified timeout. */ ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, duration_ms, ¶ms, ble_gap_test_util_disc_cb, NULL, -1, 0); /* Ensure next GAP event is at the expected time. */ rc = os_time_ms_to_ticks(duration_ms, &duration_ticks); TEST_ASSERT_FATAL(rc == 0); ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == duration_ticks); /* Advance duration ms; ensure timeout event was reported. */ os_time_advance(duration_ms); ble_hs_test_util_hci_ack_set( ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_ENABLE), 0); ticks_from_now = ble_gap_timer(); TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER); TEST_ASSERT(ble_gap_test_disc_event_type == BLE_GAP_EVENT_DISC_COMPLETE); /* Clear GAP event for remainder of test. */ ble_gap_test_util_reset_cb_info(); } TEST_CASE_SELF(ble_gap_test_case_update_timeout) { struct ble_gap_upd_params params = { .itvl_min = 10, .itvl_max = 100, .supervision_timeout = 200, .min_ce_len = 123, .max_ce_len = 456, }; /* No L2CAP. */ ble_gap_test_util_update_no_l2cap_tmo(¶ms, 1); /* L2CAP - Local unsupported; L2CAP timeout. */ ble_gap_test_util_update_l2cap_tmo(¶ms, BLE_ERR_UNKNOWN_HCI_CMD, 0, 0); /* L2CAP - Remote unsupported; L2CAP timeout. */ ble_gap_test_util_update_l2cap_tmo(¶ms, 0, BLE_ERR_UNSUPP_REM_FEATURE, 0); /* L2CAP - Remote unsupported; LL timeout. */ ble_gap_test_util_update_l2cap_tmo(¶ms, 0, BLE_ERR_UNSUPP_REM_FEATURE, 1); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_timeout_conn_forever) { ble_gap_test_util_init(); /* 30 ms. */ ble_gap_test_util_conn_timeout(30); /* No timeout. */ ble_gap_test_util_conn_forever(); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_timeout_conn_timeout) { ble_gap_test_util_init(); /* 30 ms. */ ble_gap_test_util_conn_timeout(30); /* 20 ms. */ ble_gap_test_util_conn_timeout(20); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_forever_conn_timeout) { ble_gap_test_util_init(); /* No timeout. */ ble_gap_test_util_conn_forever(); /* Cancel connect procedure manually. */ ble_gap_test_util_conn_cancel(0); /* Clear GAP event for remainder of test. */ ble_gap_test_util_reset_cb_info(); /* 30 ms. */ ble_gap_test_util_conn_timeout(30); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_timeout_disc_forever) { ble_gap_test_util_init(); /* 30 ms. */ ble_gap_test_util_disc_timeout(30); /* No timeout. */ ble_gap_test_util_disc_forever(); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_timeout_disc_timeout) { ble_gap_test_util_init(); /* 30 ms. */ ble_gap_test_util_disc_timeout(30); /* 20 ms. */ ble_gap_test_util_disc_timeout(20); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_disc_forever_disc_timeout) { ble_gap_test_util_init(); /* No timeout. */ ble_gap_test_util_disc_forever(); /* Cancel discovery procedure manually. */ ble_hs_test_util_disc_cancel(0); /* 30 ms. */ ble_gap_test_util_disc_timeout(30); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_conn_timeout_disc_timeout) { ble_gap_test_util_init(); /* 15 seconds. */ ble_gap_test_util_conn_timeout(15000); /* 1280 ms. */ ble_gap_test_util_disc_timeout(1280); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_timeout) { ble_gap_test_case_conn_timeout_conn_forever(); ble_gap_test_case_conn_timeout_conn_timeout(); ble_gap_test_case_conn_forever_conn_timeout(); ble_gap_test_case_disc_timeout_disc_forever(); ble_gap_test_case_disc_timeout_disc_timeout(); ble_gap_test_case_disc_forever_disc_timeout(); ble_gap_test_case_conn_timeout_disc_timeout(); ble_gap_test_case_update_timeout(); } TEST_CASE_SELF(ble_gap_test_case_mtu_us) { const uint8_t peer_addr[6] = { 1,2,3,4,5,6 }; int rc; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); ble_att_set_preferred_mtu(200); rc = ble_gattc_exchange_mtu(2, NULL, NULL); TEST_ASSERT_FATAL(rc == 0); ble_hs_test_util_verify_tx_mtu_cmd(1, 200); rc = ble_hs_test_util_rx_att_mtu_cmd(2, 0, 123); TEST_ASSERT_FATAL(rc == 0); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_MTU); TEST_ASSERT(ble_gap_test_event.mtu.conn_handle == 2); TEST_ASSERT(ble_gap_test_event.mtu.channel_id == BLE_L2CAP_CID_ATT); TEST_ASSERT(ble_gap_test_event.mtu.value == 123); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_mtu_peer) { const uint8_t peer_addr[6] = { 1,2,3,4,5,6 }; int rc; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); ble_att_set_preferred_mtu(200); rc = ble_hs_test_util_rx_att_mtu_cmd(2, 1, 123); TEST_ASSERT_FATAL(rc == 0); ble_hs_test_util_verify_tx_mtu_cmd(0, 200); TEST_ASSERT(ble_gap_test_event.type == BLE_GAP_EVENT_MTU); TEST_ASSERT(ble_gap_test_event.mtu.conn_handle == 2); TEST_ASSERT(ble_gap_test_event.mtu.channel_id == BLE_L2CAP_CID_ATT); TEST_ASSERT(ble_gap_test_event.mtu.value == 123); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_mtu) { ble_gap_test_case_mtu_us(); ble_gap_test_case_mtu_peer(); } /***************************************************************************** * $set cb * *****************************************************************************/ static int ble_gap_test_util_set_cb_event(struct ble_gap_event *event, void *arg) { struct ble_gap_event *event_arg; event_arg = arg; *event_arg = *event; return 0; } TEST_CASE_SELF(ble_gap_test_case_set_cb_good) { const uint8_t peer_addr[6] = { 1,2,3,4,5,6 }; struct ble_gap_event event; int rc; ble_gap_test_util_init(); ble_hs_test_util_create_conn(2, peer_addr, ble_gap_test_util_connect_cb, NULL); /* Reconfigure the callback. */ rc = ble_gap_set_event_cb(2, ble_gap_test_util_set_cb_event, &event); TEST_ASSERT_FATAL(rc == 0); /* Terminate the connection and ensure the new callback gets called. */ rc = ble_hs_test_util_conn_terminate(2, 0); TEST_ASSERT_FATAL(rc == 0); ble_hs_test_util_hci_rx_disconn_complete_event(2, 0, BLE_ERR_REM_USER_CONN_TERM); TEST_ASSERT(event.type == BLE_GAP_EVENT_DISCONNECT); TEST_ASSERT(event.disconnect.reason == BLE_HS_HCI_ERR(BLE_ERR_REM_USER_CONN_TERM)); TEST_ASSERT(event.disconnect.conn.conn_handle == 2); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_CASE_SELF(ble_gap_test_case_set_cb_bad) { int rc; ble_gap_test_util_init(); /* Ensure error is reported when specified connection doesn't exist. */ rc = ble_gap_set_event_cb(123, ble_gap_test_util_set_cb_event, NULL); TEST_ASSERT(rc == BLE_HS_ENOTCONN); ble_hs_test_util_assert_mbufs_freed(NULL); } TEST_SUITE(ble_gap_test_suite_set_cb) { ble_gap_test_case_set_cb_good(); ble_gap_test_case_set_cb_bad(); }