/*
|
* 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 <string.h>
|
#include <errno.h>
|
#include "sysinit/sysinit.h"
|
#include "stats/stats.h"
|
#include "testutil/testutil.h"
|
#include "nimble/ble.h"
|
#include "nimble/hci_common.h"
|
#include "host/ble_hs_adv.h"
|
#include "host/ble_hs_id.h"
|
#include "store/config/ble_store_config.h"
|
#include "ble_hs_test_util.h"
|
|
/* Our global device address. */
|
uint8_t g_dev_addr[BLE_DEV_ADDR_LEN];
|
|
static STAILQ_HEAD(, os_mbuf_pkthdr) ble_hs_test_util_prev_tx_queue;
|
struct os_mbuf *ble_hs_test_util_prev_tx_cur;
|
|
int ble_sm_test_store_obj_type;
|
union ble_store_key ble_sm_test_store_key;
|
union ble_store_value ble_sm_test_store_value;
|
|
const struct ble_gap_adv_params ble_hs_test_util_adv_params = {
|
.conn_mode = BLE_GAP_CONN_MODE_UND,
|
.disc_mode = BLE_GAP_DISC_MODE_GEN,
|
|
.itvl_min = 0,
|
.itvl_max = 0,
|
.channel_map = 0,
|
.filter_policy = 0,
|
.high_duty_cycle = 0,
|
};
|
|
void
|
ble_hs_test_util_prev_tx_enqueue(struct os_mbuf *om)
|
{
|
struct os_mbuf_pkthdr *omp;
|
|
assert(OS_MBUF_IS_PKTHDR(om));
|
|
omp = OS_MBUF_PKTHDR(om);
|
if (STAILQ_EMPTY(&ble_hs_test_util_prev_tx_queue)) {
|
STAILQ_INSERT_HEAD(&ble_hs_test_util_prev_tx_queue, omp, omp_next);
|
} else {
|
STAILQ_INSERT_TAIL(&ble_hs_test_util_prev_tx_queue, omp, omp_next);
|
}
|
}
|
|
static struct os_mbuf *
|
ble_hs_test_util_prev_tx_dequeue_once(struct hci_data_hdr *out_hci_hdr)
|
{
|
struct os_mbuf_pkthdr *omp;
|
struct os_mbuf *om;
|
int rc;
|
|
omp = STAILQ_FIRST(&ble_hs_test_util_prev_tx_queue);
|
if (omp == NULL) {
|
return NULL;
|
}
|
STAILQ_REMOVE_HEAD(&ble_hs_test_util_prev_tx_queue, omp_next);
|
|
om = OS_MBUF_PKTHDR_TO_MBUF(omp);
|
|
rc = ble_hs_hci_util_data_hdr_strip(om, out_hci_hdr);
|
TEST_ASSERT_FATAL(rc == 0);
|
TEST_ASSERT_FATAL(out_hci_hdr->hdh_len == OS_MBUF_PKTLEN(om));
|
|
return om;
|
}
|
|
struct os_mbuf *
|
ble_hs_test_util_prev_tx_dequeue(void)
|
{
|
struct ble_l2cap_hdr l2cap_hdr;
|
struct hci_data_hdr hci_hdr;
|
struct os_mbuf *om;
|
uint8_t pb;
|
int rc;
|
|
rc = os_mbuf_free_chain(ble_hs_test_util_prev_tx_cur);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
om = ble_hs_test_util_prev_tx_dequeue_once(&hci_hdr);
|
if (om != NULL) {
|
pb = BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc);
|
TEST_ASSERT_FATAL(pb == BLE_HCI_PB_FIRST_NON_FLUSH);
|
|
rc = ble_l2cap_parse_hdr(om, 0, &l2cap_hdr);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
os_mbuf_adj(om, BLE_L2CAP_HDR_SZ);
|
|
ble_hs_test_util_prev_tx_cur = om;
|
while (OS_MBUF_PKTLEN(ble_hs_test_util_prev_tx_cur) <
|
l2cap_hdr.len) {
|
|
om = ble_hs_test_util_prev_tx_dequeue_once(&hci_hdr);
|
TEST_ASSERT_FATAL(om != NULL);
|
|
pb = BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc);
|
TEST_ASSERT_FATAL(pb == BLE_HCI_PB_MIDDLE);
|
|
os_mbuf_concat(ble_hs_test_util_prev_tx_cur, om);
|
}
|
} else {
|
ble_hs_test_util_prev_tx_cur = NULL;
|
}
|
|
return ble_hs_test_util_prev_tx_cur;
|
}
|
|
struct os_mbuf *
|
ble_hs_test_util_prev_tx_dequeue_pullup(void)
|
{
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
if (om != NULL) {
|
om = os_mbuf_pullup(om, OS_MBUF_PKTLEN(om));
|
TEST_ASSERT_FATAL(om != NULL);
|
ble_hs_test_util_prev_tx_cur = om;
|
}
|
|
return om;
|
}
|
|
int
|
ble_hs_test_util_prev_tx_queue_sz(void)
|
{
|
struct os_mbuf_pkthdr *omp;
|
int cnt;
|
|
cnt = 0;
|
STAILQ_FOREACH(omp, &ble_hs_test_util_prev_tx_queue, omp_next) {
|
cnt++;
|
}
|
|
return cnt;
|
}
|
|
void
|
ble_hs_test_util_prev_tx_queue_clear(void)
|
{
|
while (!STAILQ_EMPTY(&ble_hs_test_util_prev_tx_queue)) {
|
ble_hs_test_util_prev_tx_dequeue();
|
}
|
}
|
|
static void
|
ble_hs_test_util_conn_params_dflt(struct ble_gap_conn_params *conn_params)
|
{
|
conn_params->scan_itvl = 0x0010;
|
conn_params->scan_window = 0x0010;
|
conn_params->itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN;
|
conn_params->itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX;
|
conn_params->latency = BLE_GAP_INITIAL_CONN_LATENCY;
|
conn_params->supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT;
|
conn_params->min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
|
conn_params->max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
|
}
|
|
static void
|
ble_hs_test_util_hcc_from_conn_params(
|
struct hci_create_conn *hcc, uint8_t own_addr_type,
|
const ble_addr_t *peer_addr, const struct ble_gap_conn_params *conn_params)
|
{
|
hcc->scan_itvl = conn_params->scan_itvl;
|
hcc->scan_window = conn_params->scan_window;
|
|
if (peer_addr == NULL) {
|
hcc->filter_policy = BLE_HCI_CONN_FILT_USE_WL;
|
hcc->peer_addr_type = 0;
|
memset(hcc->peer_addr, 0, 6);
|
} else {
|
hcc->filter_policy = BLE_HCI_CONN_FILT_NO_WL;
|
hcc->peer_addr_type = peer_addr->type;
|
memcpy(hcc->peer_addr, peer_addr->val, 6);
|
}
|
hcc->own_addr_type = own_addr_type;
|
hcc->conn_itvl_min = conn_params->itvl_min;
|
hcc->conn_itvl_max = conn_params->itvl_max;
|
hcc->conn_latency = conn_params->latency;
|
hcc->supervision_timeout = conn_params->supervision_timeout;
|
hcc->min_ce_len = conn_params->min_ce_len;
|
hcc->max_ce_len = conn_params->max_ce_len;
|
}
|
|
void
|
ble_hs_test_util_create_rpa_conn(uint16_t handle, uint8_t own_addr_type,
|
const uint8_t *our_rpa,
|
uint8_t peer_addr_type,
|
const uint8_t *peer_id_addr,
|
const uint8_t *peer_rpa,
|
uint8_t conn_features,
|
ble_gap_event_fn *cb, void *cb_arg)
|
{
|
ble_addr_t addr;
|
struct ble_gap_conn_complete evt;
|
struct ble_hci_ev_le_subev_rd_rem_used_feat evt2;
|
int rc;
|
|
addr.type = peer_addr_type;
|
memcpy(addr.val, peer_id_addr, 6);
|
|
rc = ble_hs_test_util_connect(own_addr_type, &addr, 0, NULL, cb, cb_arg,
|
0);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
/* 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 = BLE_ERR_SUCCESS;
|
evt.connection_handle = handle;
|
evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER;
|
evt.peer_addr_type = peer_addr_type;
|
memcpy(evt.peer_addr, peer_id_addr, 6);
|
evt.conn_itvl = BLE_GAP_INITIAL_CONN_ITVL_MAX;
|
evt.conn_latency = BLE_GAP_INITIAL_CONN_LATENCY;
|
evt.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT;
|
memcpy(evt.local_rpa, our_rpa, 6);
|
memcpy(evt.peer_rpa, peer_rpa, 6);
|
|
rc = ble_gap_rx_conn_complete(&evt, 0);
|
TEST_ASSERT(rc == 0);
|
|
evt2.subev_code = BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT;
|
evt2.status = BLE_ERR_SUCCESS;
|
evt2.conn_handle = htole16(handle);
|
memcpy(evt2.features, ((uint8_t[]){ conn_features, 0, 0, 0, 0, 0, 0, 0 }),
|
8);
|
|
ble_gap_rx_rd_rem_sup_feat_complete(&evt2);
|
|
ble_hs_test_util_hci_out_clear();
|
}
|
|
void
|
ble_hs_test_util_create_conn(uint16_t handle, const uint8_t *peer_id_addr,
|
ble_gap_event_fn *cb, void *cb_arg)
|
{
|
static uint8_t null_addr[6];
|
|
ble_hs_test_util_create_rpa_conn(handle, BLE_OWN_ADDR_PUBLIC, null_addr,
|
BLE_ADDR_PUBLIC, peer_id_addr,
|
null_addr, BLE_HS_TEST_CONN_FEAT_ALL,
|
cb, cb_arg);
|
}
|
|
void
|
ble_hs_test_util_create_conn_feat(uint16_t handle, const uint8_t *peer_id_addr,
|
uint8_t conn_features, ble_gap_event_fn *cb,
|
void *cb_arg)
|
{
|
static uint8_t null_addr[6];
|
|
ble_hs_test_util_create_rpa_conn(handle, BLE_OWN_ADDR_PUBLIC, null_addr,
|
BLE_ADDR_PUBLIC, peer_id_addr,
|
null_addr, conn_features, cb, cb_arg);
|
}
|
|
int
|
ble_hs_test_util_connect(uint8_t own_addr_type, const ble_addr_t *peer_addr,
|
int32_t duration_ms,
|
const struct ble_gap_conn_params *params,
|
ble_gap_event_fn *cb, void *cb_arg,
|
uint8_t ack_status)
|
{
|
struct ble_gap_conn_params dflt_params;
|
struct hci_create_conn hcc;
|
int rc;
|
|
/* This function ensures the most recently sent HCI command is the expected
|
* create connection command. If the current test case has unverified HCI
|
* commands, assume we are not interested in them and clear the queue.
|
*/
|
ble_hs_test_util_hci_out_clear();
|
|
ble_hs_test_util_hci_ack_set(
|
ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
|
BLE_HCI_OCF_LE_CREATE_CONN),
|
ack_status);
|
|
rc = ble_gap_connect(own_addr_type, peer_addr, duration_ms, params, cb,
|
cb_arg);
|
if (ack_status != 0) {
|
TEST_ASSERT(rc == BLE_HS_HCI_ERR(ack_status));
|
} else if (rc != 0) {
|
return rc;
|
}
|
|
if (params == NULL) {
|
ble_hs_test_util_conn_params_dflt(&dflt_params);
|
params = &dflt_params;
|
}
|
|
ble_hs_test_util_hcc_from_conn_params(&hcc, own_addr_type, peer_addr,
|
params);
|
ble_hs_test_util_hci_verify_tx_create_conn(&hcc);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_conn_cancel(uint8_t ack_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set(
|
ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
|
BLE_HCI_OCF_LE_CREATE_CONN_CANCEL),
|
ack_status);
|
|
rc = ble_gap_conn_cancel();
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_rx_conn_cancel_evt(void)
|
{
|
ble_hs_test_util_conn_cancel(0);
|
ble_hs_test_util_hci_rx_conn_cancel_evt();
|
}
|
|
void
|
ble_hs_test_util_conn_cancel_full(void)
|
{
|
ble_hs_test_util_conn_cancel(0);
|
ble_hs_test_util_rx_conn_cancel_evt();
|
}
|
|
int
|
ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set_disconnect(hci_status);
|
rc = ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_conn_disconnect(uint16_t conn_handle)
|
{
|
int rc;
|
|
rc = ble_hs_test_util_conn_terminate(conn_handle, 0);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
/* Receive disconnection complete event. */
|
ble_hs_test_util_hci_rx_disconn_complete_event(conn_handle, 0,
|
BLE_ERR_CONN_TERM_LOCAL);
|
}
|
|
int
|
ble_hs_test_util_disc(uint8_t own_addr_type, int32_t duration_ms,
|
const struct ble_gap_disc_params *disc_params,
|
ble_gap_event_fn *cb, void *cb_arg, int fail_idx,
|
uint8_t fail_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set_disc(own_addr_type, fail_idx, fail_status);
|
rc = ble_gap_disc(own_addr_type, duration_ms, disc_params,
|
cb, cb_arg);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_disc_cancel(uint8_t ack_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set(
|
ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
|
BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
|
ack_status);
|
|
rc = ble_gap_disc_cancel();
|
return rc;
|
}
|
|
static void
|
ble_hs_test_util_verify_tx_rd_pwr(void)
|
{
|
uint8_t param_len;
|
|
ble_hs_test_util_hci_verify_tx(BLE_HCI_OGF_LE,
|
BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR,
|
¶m_len);
|
TEST_ASSERT(param_len == 0);
|
}
|
|
int
|
ble_hs_test_util_adv_set_fields(const struct ble_hs_adv_fields *adv_fields,
|
int cmd_fail_idx, uint8_t hci_status)
|
{
|
struct ble_hs_test_util_hci_ack acks[3];
|
int auto_pwr;
|
int rc;
|
int i;
|
|
auto_pwr = adv_fields->tx_pwr_lvl_is_present &&
|
adv_fields->tx_pwr_lvl == BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
|
i = 0;
|
if (auto_pwr) {
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR),
|
ble_hs_test_util_hci_misc_exp_status(i, cmd_fail_idx, hci_status),
|
{0},
|
1,
|
};
|
i++;
|
}
|
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_DATA),
|
ble_hs_test_util_hci_misc_exp_status(i, cmd_fail_idx, hci_status),
|
};
|
i++;
|
|
memset(acks + i, 0, sizeof acks[i]);
|
ble_hs_test_util_hci_ack_set_seq(acks);
|
|
rc = ble_gap_adv_set_fields(adv_fields);
|
if (rc == 0 && auto_pwr) {
|
/* Verify tx of set advertising params command. */
|
ble_hs_test_util_verify_tx_rd_pwr();
|
}
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_adv_rsp_set_fields(const struct ble_hs_adv_fields *adv_fields,
|
int cmd_fail_idx, uint8_t hci_status)
|
{
|
struct ble_hs_test_util_hci_ack acks[3];
|
int auto_pwr;
|
int rc;
|
int i;
|
|
auto_pwr = adv_fields->tx_pwr_lvl_is_present &&
|
adv_fields->tx_pwr_lvl == BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
|
i = 0;
|
if (auto_pwr) {
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR),
|
ble_hs_test_util_hci_misc_exp_status(i, cmd_fail_idx, hci_status),
|
{0},
|
1,
|
};
|
i++;
|
}
|
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA),
|
ble_hs_test_util_hci_misc_exp_status(i, cmd_fail_idx, hci_status),
|
};
|
i++;
|
|
memset(acks + i, 0, sizeof acks[i]);
|
ble_hs_test_util_hci_ack_set_seq(acks);
|
|
rc = ble_gap_adv_rsp_set_fields(adv_fields);
|
if (rc == 0 && auto_pwr) {
|
/* Verify tx of set advertising params command. */
|
ble_hs_test_util_verify_tx_rd_pwr();
|
}
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_adv_start(uint8_t own_addr_type, const ble_addr_t *peer_addr,
|
const struct ble_gap_adv_params *adv_params,
|
int32_t duration_ms,
|
ble_gap_event_fn *cb, void *cb_arg,
|
int fail_idx, uint8_t fail_status)
|
{
|
struct ble_hs_test_util_hci_ack acks[6];
|
int rc;
|
int i;
|
|
i = 0;
|
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_PARAMS),
|
fail_idx == i ? fail_status : 0,
|
};
|
i++;
|
|
acks[i] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
|
ble_hs_test_util_hci_misc_exp_status(i, fail_idx, fail_status),
|
};
|
i++;
|
|
memset(acks + i, 0, sizeof acks[i]);
|
|
ble_hs_test_util_hci_ack_set_seq(acks);
|
|
rc = ble_gap_adv_start(own_addr_type, peer_addr,
|
duration_ms, adv_params, cb, cb_arg);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_adv_stop(uint8_t hci_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set(
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
|
hci_status);
|
|
rc = ble_gap_adv_stop();
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_wl_set(ble_addr_t *addrs, uint8_t addrs_count,
|
int fail_idx, uint8_t fail_status)
|
{
|
struct ble_hs_test_util_hci_ack acks[64];
|
int cmd_idx;
|
int rc;
|
int i;
|
|
TEST_ASSERT_FATAL(addrs_count < 63);
|
|
cmd_idx = 0;
|
acks[cmd_idx] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CLEAR_WHITE_LIST),
|
ble_hs_test_util_hci_misc_exp_status(cmd_idx, fail_idx, fail_status),
|
};
|
cmd_idx++;
|
|
for (i = 0; i < addrs_count; i++) {
|
acks[cmd_idx] = (struct ble_hs_test_util_hci_ack) {
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_ADD_WHITE_LIST),
|
ble_hs_test_util_hci_misc_exp_status(cmd_idx, fail_idx, fail_status),
|
};
|
|
cmd_idx++;
|
}
|
memset(acks + cmd_idx, 0, sizeof acks[cmd_idx]);
|
|
ble_hs_test_util_hci_ack_set_seq(acks);
|
rc = ble_gap_wl_set(addrs, addrs_count);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_conn_update(uint16_t conn_handle,
|
struct ble_gap_upd_params *params,
|
uint8_t hci_status)
|
{
|
int rc;
|
|
/*
|
* 0xFF is magic value used for cases where we expect update over L2CAP to
|
* be triggered - in this case we don't need phony ack.
|
*/
|
if (hci_status != 0xFF) {
|
ble_hs_test_util_hci_ack_set(
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CONN_UPDATE),
|
hci_status);
|
}
|
|
rc = ble_gap_update_params(conn_handle, params);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_set_our_irk(const uint8_t *irk, int fail_idx,
|
uint8_t hci_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set_seq(((struct ble_hs_test_util_hci_ack[]) {
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
|
ble_hs_test_util_hci_misc_exp_status(0, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CLR_RESOLV_LIST),
|
ble_hs_test_util_hci_misc_exp_status(1, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
|
ble_hs_test_util_hci_misc_exp_status(2, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
|
ble_hs_test_util_hci_misc_exp_status(3, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
|
ble_hs_test_util_hci_misc_exp_status(4, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_PRIVACY_MODE),
|
ble_hs_test_util_hci_misc_exp_status(5, fail_idx, hci_status),
|
},
|
{
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_PRIVACY_MODE),
|
ble_hs_test_util_hci_misc_exp_status(6, fail_idx, hci_status),
|
},
|
{
|
0
|
}
|
}));
|
|
rc = ble_hs_pvcy_set_our_irk(irk);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_security_initiate(uint16_t conn_handle, uint8_t hci_status)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set(
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_START_ENCRYPT), hci_status);
|
|
rc = ble_gap_security_initiate(conn_handle);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_l2cap_rx_first_frag(uint16_t conn_handle, uint16_t cid,
|
struct hci_data_hdr *hci_hdr,
|
struct os_mbuf *om)
|
{
|
int rc;
|
|
om = ble_l2cap_prepend_hdr(om, cid, OS_MBUF_PKTLEN(om));
|
TEST_ASSERT_FATAL(om != NULL);
|
|
rc = ble_hs_test_util_l2cap_rx(conn_handle, hci_hdr, om);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_l2cap_rx(uint16_t conn_handle,
|
struct hci_data_hdr *hci_hdr,
|
struct os_mbuf *om)
|
{
|
struct ble_hs_conn *conn;
|
ble_l2cap_rx_fn *rx_cb;
|
int reject_cid;
|
int rc;
|
|
ble_hs_lock();
|
|
conn = ble_hs_conn_find(conn_handle);
|
if (conn != NULL) {
|
rc = ble_l2cap_rx(conn, hci_hdr, om, &rx_cb, &reject_cid);
|
} else {
|
rc = os_mbuf_free_chain(om);
|
TEST_ASSERT_FATAL(rc == 0);
|
}
|
|
ble_hs_unlock();
|
|
if (conn == NULL) {
|
rc = BLE_HS_ENOTCONN;
|
} else if (rc == 0) {
|
TEST_ASSERT_FATAL(rx_cb != NULL);
|
rc = rx_cb(conn->bhc_rx_chan);
|
ble_l2cap_remove_rx(conn, conn->bhc_rx_chan);
|
} else if (rc == BLE_HS_EAGAIN) {
|
/* More fragments on the way. */
|
rc = 0;
|
} else {
|
if (reject_cid != -1) {
|
ble_l2cap_sig_reject_invalid_cid_tx(conn_handle, 0, 0, reject_cid);
|
}
|
}
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_l2cap_rx_payload_flat(uint16_t conn_handle, uint16_t cid,
|
const void *data, int len)
|
{
|
struct hci_data_hdr hci_hdr;
|
struct os_mbuf *om;
|
int rc;
|
|
om = ble_hs_mbuf_l2cap_pkt();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
rc = os_mbuf_append(om, data, len);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
hci_hdr.hdh_handle_pb_bc =
|
ble_hs_hci_util_handle_pb_bc_join(conn_handle,
|
BLE_HCI_PB_FIRST_FLUSH, 0);
|
hci_hdr.hdh_len = OS_MBUF_PKTHDR(om)->omp_len;
|
|
rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, cid, &hci_hdr, om);
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_set_att_mtu(uint16_t conn_handle, uint16_t mtu)
|
{
|
struct ble_l2cap_chan *chan;
|
struct ble_hs_conn *conn;
|
int rc;
|
|
if (mtu <= BLE_ATT_MTU_DFLT) {
|
return;
|
}
|
|
ble_hs_lock();
|
|
rc = ble_att_conn_chan_find(conn_handle, &conn, &chan);
|
assert(rc == 0);
|
chan->my_mtu = mtu;
|
chan->peer_mtu = mtu;
|
chan->flags |= BLE_L2CAP_CHAN_F_TXED_MTU;
|
|
ble_hs_unlock();
|
}
|
|
int
|
ble_hs_test_util_rx_att_mtu_cmd(uint16_t conn_handle, int is_req, uint16_t mtu)
|
{
|
struct ble_att_mtu_cmd cmd;
|
uint8_t buf[BLE_ATT_MTU_CMD_SZ];
|
int rc;
|
|
cmd.bamc_mtu = mtu;
|
|
if (is_req) {
|
ble_att_mtu_req_write(buf, sizeof buf, &cmd);
|
} else {
|
ble_att_mtu_rsp_write(buf, sizeof buf, &cmd);
|
}
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, sizeof buf);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_find_info_req(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle)
|
{
|
struct ble_att_find_info_req req;
|
uint8_t buf[BLE_ATT_FIND_INFO_REQ_SZ];
|
int rc;
|
|
req.bafq_start_handle = start_handle;
|
req.bafq_end_handle = end_handle;
|
|
ble_att_find_info_req_write(buf, sizeof buf, &req);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, sizeof buf);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_find_type_value_req(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle,
|
uint16_t attr_type,
|
const void *attr_val,
|
uint16_t attr_len)
|
{
|
struct ble_att_find_type_value_req req;
|
uint8_t buf[BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ + 16];
|
int rc;
|
|
TEST_ASSERT(attr_len <= 16);
|
|
req.bavq_start_handle = start_handle;
|
req.bavq_end_handle = end_handle;
|
req.bavq_attr_type = attr_type;
|
|
ble_att_find_type_value_req_write(buf, sizeof buf, &req);
|
memcpy(buf + BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_type_req(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle,
|
const ble_uuid_t *uuid)
|
{
|
struct ble_att_read_type_req req;
|
uint8_t buf[BLE_ATT_READ_TYPE_REQ_SZ_128];
|
int req_len;
|
int rc;
|
|
req.batq_start_handle = start_handle;
|
req.batq_end_handle = end_handle;
|
|
ble_att_read_type_req_write(buf, sizeof buf, &req);
|
|
ble_uuid_flat(uuid, buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ);
|
req_len = BLE_ATT_READ_TYPE_REQ_BASE_SZ + ble_uuid_length(uuid);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, req_len);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_type_req16(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle,
|
uint16_t uuid16)
|
{
|
int rc;
|
|
rc = ble_hs_test_util_rx_att_read_type_req(conn_handle, start_handle,
|
end_handle,
|
BLE_UUID16_DECLARE(uuid16));
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_req(uint16_t conn_handle, uint16_t attr_handle)
|
{
|
struct ble_att_read_req req;
|
uint8_t buf[BLE_ATT_READ_REQ_SZ];
|
int rc;
|
|
req.barq_handle = attr_handle;
|
ble_att_read_req_write(buf, sizeof buf, &req);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, sizeof buf);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_blob_req(uint16_t conn_handle,
|
uint16_t attr_handle,
|
uint16_t offset)
|
{
|
struct ble_att_read_blob_req req;
|
uint8_t buf[BLE_ATT_READ_BLOB_REQ_SZ];
|
int rc;
|
|
req.babq_handle = attr_handle;
|
req.babq_offset = offset;
|
ble_att_read_blob_req_write(buf, sizeof buf, &req);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, sizeof buf);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_mult_req(uint16_t conn_handle,
|
const uint16_t *handles,
|
int num_handles)
|
{
|
uint8_t buf[256];
|
int off;
|
int rc;
|
int i;
|
|
ble_att_read_mult_req_write(buf, sizeof buf);
|
|
off = BLE_ATT_READ_MULT_REQ_BASE_SZ;
|
for (i = 0; i < num_handles; i++) {
|
put_le16(buf + off, handles[i]);
|
off += 2;
|
}
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, off);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_group_type_req(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle,
|
const ble_uuid_t *uuid)
|
{
|
struct ble_att_read_group_type_req req;
|
uint8_t buf[BLE_ATT_READ_GROUP_TYPE_REQ_SZ_128];
|
int req_len;
|
int rc;
|
|
req.bagq_start_handle = start_handle;
|
req.bagq_end_handle = end_handle;
|
|
ble_uuid_flat(uuid, buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ);
|
req_len = BLE_ATT_READ_GROUP_TYPE_REQ_BASE_SZ + ble_uuid_length(uuid);
|
|
ble_att_read_group_type_req_write(buf, sizeof buf, &req);
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, req_len);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_read_group_type_req16(uint16_t conn_handle,
|
uint16_t start_handle,
|
uint16_t end_handle,
|
uint16_t uuid16)
|
{
|
int rc;
|
|
rc = ble_hs_test_util_rx_att_read_group_type_req(conn_handle, start_handle,
|
end_handle,
|
BLE_UUID16_DECLARE(uuid16));
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_write_req(uint16_t conn_handle, uint16_t attr_handle,
|
const void *attr_val, uint16_t attr_len)
|
{
|
struct ble_att_write_req req;
|
uint8_t buf[BLE_ATT_WRITE_REQ_BASE_SZ + BLE_ATT_ATTR_MAX_LEN];
|
int rc;
|
|
req.bawq_handle = attr_handle;
|
ble_att_write_req_write(buf, sizeof buf, &req);
|
|
memcpy(buf + BLE_ATT_WRITE_REQ_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_WRITE_REQ_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_write_cmd(uint16_t conn_handle, uint16_t attr_handle,
|
const void *attr_val, uint16_t attr_len)
|
{
|
struct ble_att_write_req req;
|
uint8_t buf[BLE_ATT_WRITE_REQ_BASE_SZ + BLE_ATT_ATTR_MAX_LEN];
|
int rc;
|
|
req.bawq_handle = attr_handle;
|
ble_att_write_cmd_write(buf, sizeof buf, &req);
|
|
memcpy(buf + BLE_ATT_WRITE_REQ_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_WRITE_REQ_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_prep_write_req(uint16_t conn_handle,
|
uint16_t attr_handle,
|
uint16_t offset,
|
const void *attr_val,
|
uint16_t attr_len)
|
{
|
struct ble_att_prep_write_cmd prep_req;
|
uint8_t buf[BLE_ATT_PREP_WRITE_CMD_BASE_SZ + BLE_ATT_ATTR_MAX_LEN];
|
int rc;
|
|
prep_req.bapc_handle = attr_handle;
|
prep_req.bapc_offset = offset;
|
ble_att_prep_write_req_write(buf, sizeof buf, &prep_req);
|
memcpy(buf + BLE_ATT_PREP_WRITE_CMD_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_PREP_WRITE_CMD_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_exec_write_req(uint16_t conn_handle, uint8_t flags)
|
{
|
struct ble_att_exec_write_req exec_req;
|
uint8_t buf[BLE_ATT_EXEC_WRITE_REQ_SZ];
|
int rc;
|
|
exec_req.baeq_flags = flags;
|
ble_att_exec_write_req_write(buf, sizeof buf, &exec_req);
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf,
|
BLE_ATT_EXEC_WRITE_REQ_SZ);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_notify_req(uint16_t conn_handle,
|
uint16_t attr_handle,
|
void *attr_val,
|
uint16_t attr_len)
|
{
|
struct ble_att_notify_req req;
|
uint8_t buf[BLE_ATT_NOTIFY_REQ_BASE_SZ + BLE_ATT_ATTR_MAX_LEN];
|
int rc;
|
|
req.banq_handle = attr_handle;
|
ble_att_notify_req_write(buf, sizeof buf, &req);
|
memcpy(buf + BLE_ATT_NOTIFY_REQ_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_NOTIFY_REQ_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_rx_att_indicate_req(uint16_t conn_handle,
|
uint16_t attr_handle,
|
void *attr_val,
|
uint16_t attr_len)
|
{
|
struct ble_att_indicate_req req;
|
uint8_t buf[BLE_ATT_INDICATE_REQ_BASE_SZ + BLE_ATT_ATTR_MAX_LEN];
|
int rc;
|
|
req.baiq_handle = attr_handle;
|
ble_att_indicate_req_write(buf, sizeof buf, &req);
|
memcpy(buf + BLE_ATT_INDICATE_REQ_BASE_SZ, attr_val, attr_len);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(
|
conn_handle, BLE_L2CAP_CID_ATT, buf,
|
BLE_ATT_INDICATE_REQ_BASE_SZ + attr_len);
|
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_rx_att_err_rsp(uint16_t conn_handle, uint8_t req_op,
|
uint8_t error_code, uint16_t err_handle)
|
{
|
struct ble_att_error_rsp rsp;
|
uint8_t buf[BLE_ATT_ERROR_RSP_SZ];
|
int rc;
|
|
rsp.baep_req_op = req_op;
|
rsp.baep_handle = err_handle;
|
rsp.baep_error_code = error_code;
|
|
ble_att_error_rsp_write(buf, sizeof buf, &rsp);
|
|
rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
|
buf, sizeof buf);
|
TEST_ASSERT(rc == 0);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_prep_write(uint16_t attr_handle, uint16_t offset,
|
const void *data, int data_len)
|
{
|
struct ble_att_prep_write_cmd req;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
TEST_ASSERT_FATAL(om != NULL);
|
TEST_ASSERT(OS_MBUF_PKTLEN(om) ==
|
BLE_ATT_PREP_WRITE_CMD_BASE_SZ + data_len);
|
|
om = os_mbuf_pullup(om, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
|
TEST_ASSERT_FATAL(om != NULL);
|
|
ble_att_prep_write_req_parse(om->om_data, om->om_len, &req);
|
TEST_ASSERT(req.bapc_handle == attr_handle);
|
TEST_ASSERT(req.bapc_offset == offset);
|
TEST_ASSERT(os_mbuf_cmpf(om, BLE_ATT_PREP_WRITE_CMD_BASE_SZ,
|
data, data_len) == 0);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_exec_write(uint8_t expected_flags)
|
{
|
struct ble_att_exec_write_req req;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om != NULL);
|
TEST_ASSERT(om->om_len == BLE_ATT_EXEC_WRITE_REQ_SZ);
|
|
ble_att_exec_write_req_parse(om->om_data, om->om_len, &req);
|
TEST_ASSERT(req.baeq_flags == expected_flags);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_find_type_value(uint16_t start_handle,
|
uint16_t end_handle,
|
uint16_t attr_type,
|
const void *value,
|
uint16_t value_len)
|
{
|
struct ble_att_find_type_value_req req;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om != NULL);
|
TEST_ASSERT(om->om_len == BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ + value_len);
|
|
ble_att_find_type_value_req_parse(om->om_data, om->om_len, &req);
|
TEST_ASSERT(req.bavq_start_handle == start_handle);
|
TEST_ASSERT(req.bavq_end_handle == end_handle);
|
TEST_ASSERT(req.bavq_attr_type == attr_type);
|
TEST_ASSERT(memcmp(om->om_data + BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ,
|
value,
|
value_len) == 0);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_disc_svc_uuid(const ble_uuid_t *uuid)
|
{
|
uint8_t uuid_buf[16];
|
|
ble_uuid_flat(uuid, uuid_buf);
|
ble_hs_test_util_verify_tx_find_type_value(
|
1, 0xffff, BLE_ATT_UUID_PRIMARY_SERVICE,
|
uuid_buf, ble_uuid_length(uuid));
|
}
|
|
void
|
ble_hs_test_util_verify_tx_read_rsp_gen(uint8_t att_op,
|
uint8_t *attr_data, int attr_len)
|
{
|
struct os_mbuf *om;
|
uint8_t u8;
|
int rc;
|
int i;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
|
rc = os_mbuf_copydata(om, 0, 1, &u8);
|
TEST_ASSERT(rc == 0);
|
TEST_ASSERT(u8 == att_op);
|
|
for (i = 0; i < attr_len; i++) {
|
rc = os_mbuf_copydata(om, i + 1, 1, &u8);
|
TEST_ASSERT(rc == 0);
|
TEST_ASSERT(u8 == attr_data[i]);
|
}
|
|
rc = os_mbuf_copydata(om, i + 1, 1, &u8);
|
TEST_ASSERT(rc != 0);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_read_rsp(uint8_t *attr_data, int attr_len)
|
{
|
ble_hs_test_util_verify_tx_read_rsp_gen(BLE_ATT_OP_READ_RSP,
|
attr_data, attr_len);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_read_blob_rsp(uint8_t *attr_data, int attr_len)
|
{
|
ble_hs_test_util_verify_tx_read_rsp_gen(BLE_ATT_OP_READ_BLOB_RSP,
|
attr_data, attr_len);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_write_rsp(void)
|
{
|
struct os_mbuf *om;
|
uint8_t u8;
|
int rc;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
|
rc = os_mbuf_copydata(om, 0, 1, &u8);
|
TEST_ASSERT(rc == 0);
|
TEST_ASSERT(u8 == BLE_ATT_OP_WRITE_RSP);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_mtu_cmd(int is_req, uint16_t mtu)
|
{
|
struct ble_att_mtu_cmd cmd;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
if (is_req) {
|
ble_att_mtu_req_parse(om->om_data, om->om_len, &cmd);
|
} else {
|
ble_att_mtu_rsp_parse(om->om_data, om->om_len, &cmd);
|
}
|
|
TEST_ASSERT(cmd.bamc_mtu == mtu);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_find_info_rsp(
|
struct ble_hs_test_util_att_info_entry *entries)
|
{
|
struct ble_hs_test_util_att_info_entry *entry;
|
struct ble_att_find_info_rsp rsp;
|
struct os_mbuf *om;
|
uint16_t handle;
|
uint8_t buf[BLE_ATT_FIND_INFO_RSP_BASE_SZ];
|
ble_uuid_any_t uuid;
|
int off;
|
int rc;
|
|
off = 0;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om);
|
|
rc = os_mbuf_copydata(om, off, sizeof buf, buf);
|
TEST_ASSERT(rc == 0);
|
off += sizeof buf;
|
|
ble_att_find_info_rsp_parse(buf, sizeof buf, &rsp);
|
|
for (entry = entries; entry->handle != 0; entry++) {
|
rc = os_mbuf_copydata(om, off, 2, &handle);
|
TEST_ASSERT(rc == 0);
|
off += 2;
|
|
handle = get_le16((void *)&handle);
|
TEST_ASSERT(handle == entry->handle);
|
|
if (entry->uuid->type == BLE_UUID_TYPE_16) {
|
TEST_ASSERT(rsp.bafp_format ==
|
BLE_ATT_FIND_INFO_RSP_FORMAT_16BIT);
|
|
ble_uuid_init_from_att_mbuf(&uuid, om, off, 2);
|
TEST_ASSERT(rc == 0);
|
off += 2;
|
} else {
|
TEST_ASSERT(rsp.bafp_format ==
|
BLE_ATT_FIND_INFO_RSP_FORMAT_128BIT);
|
|
rc = ble_uuid_init_from_att_mbuf(&uuid, om, off, 16);
|
TEST_ASSERT(rc == 0);
|
off += 16;
|
}
|
|
TEST_ASSERT(ble_uuid_cmp(entry->uuid, &uuid.u) == 0);
|
}
|
|
/* Ensure there is no extra data in the response. */
|
TEST_ASSERT(off == OS_MBUF_PKTHDR(om)->omp_len);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_read_group_type_rsp(
|
struct ble_hs_test_util_att_group_type_entry *entries)
|
{
|
struct ble_hs_test_util_att_group_type_entry *entry;
|
struct ble_att_read_group_type_rsp rsp;
|
struct os_mbuf *om;
|
uint16_t u16;
|
ble_uuid_any_t uuid;
|
int off;
|
int rc;
|
|
om = ble_hs_test_util_prev_tx_dequeue_pullup();
|
TEST_ASSERT_FATAL(om);
|
|
ble_att_read_group_type_rsp_parse(om->om_data, om->om_len, &rsp);
|
|
off = BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ;
|
for (entry = entries; entry->start_handle != 0; entry++) {
|
if (entry->uuid->type == BLE_UUID_TYPE_16) {
|
TEST_ASSERT(rsp.bagp_length ==
|
BLE_ATT_READ_GROUP_TYPE_ADATA_SZ_16);
|
} else {
|
TEST_ASSERT(rsp.bagp_length ==
|
BLE_ATT_READ_GROUP_TYPE_ADATA_SZ_128);
|
}
|
|
rc = os_mbuf_copydata(om, off, 2, &u16);
|
TEST_ASSERT(rc == 0);
|
put_le16(&u16, u16);
|
TEST_ASSERT(u16 == entry->start_handle);
|
off += 2;
|
|
rc = os_mbuf_copydata(om, off, 2, &u16);
|
TEST_ASSERT(rc == 0);
|
put_le16(&u16, u16);
|
TEST_ASSERT(u16 == entry->end_handle);
|
off += 2;
|
|
if (entry->uuid->type == BLE_UUID_TYPE_16) {
|
rc = ble_uuid_init_from_att_mbuf(&uuid, om, off, 2);
|
TEST_ASSERT(rc == 0);
|
} else {
|
rc = ble_uuid_init_from_att_mbuf(&uuid, om, off, 16);
|
TEST_ASSERT(rc == 0);
|
}
|
|
TEST_ASSERT(ble_uuid_cmp(&uuid.u, entry->uuid) == 0);
|
off += ble_uuid_length(&uuid.u);
|
}
|
|
/* Ensure there is no extra data in the response. */
|
TEST_ASSERT(off == OS_MBUF_PKTLEN(om));
|
}
|
|
void
|
ble_hs_test_util_verify_tx_err_rsp(uint8_t req_op, uint16_t handle,
|
uint8_t error_code)
|
{
|
struct ble_att_error_rsp rsp;
|
struct os_mbuf *om;
|
uint8_t buf[BLE_ATT_ERROR_RSP_SZ];
|
int rc;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
|
rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
|
TEST_ASSERT(rc == 0);
|
|
ble_att_error_rsp_parse(buf, sizeof buf, &rsp);
|
|
TEST_ASSERT(rsp.baep_req_op == req_op);
|
TEST_ASSERT(rsp.baep_handle == handle);
|
TEST_ASSERT(rsp.baep_error_code == error_code);
|
}
|
|
void
|
ble_hs_test_util_verify_tx_write_cmd(uint16_t handle, const void *data,
|
uint16_t data_len)
|
{
|
struct ble_att_write_req req;
|
struct os_mbuf *om;
|
uint8_t buf[BLE_ATT_WRITE_CMD_BASE_SZ];
|
int rc;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
|
rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
|
TEST_ASSERT(rc == 0);
|
|
ble_att_write_cmd_parse(buf, sizeof buf, &req);
|
|
TEST_ASSERT(req.bawq_handle == handle);
|
|
os_mbuf_adj(om, sizeof buf);
|
TEST_ASSERT(OS_MBUF_PKTLEN(om) == data_len);
|
TEST_ASSERT(os_mbuf_cmpf(om, 0, data, data_len) == 0);
|
}
|
|
static struct os_mbuf *
|
ble_hs_test_util_verify_tx_l2cap_sig_hdr(uint8_t op, uint8_t id,
|
uint16_t payload_len,
|
struct ble_l2cap_sig_hdr *out_hdr)
|
{
|
struct ble_l2cap_sig_hdr hdr;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
TEST_ASSERT(OS_MBUF_PKTLEN(om) == BLE_L2CAP_SIG_HDR_SZ + payload_len);
|
ble_l2cap_sig_hdr_parse(om->om_data, om->om_len, &hdr);
|
TEST_ASSERT(hdr.op == op);
|
if (id != 0) {
|
TEST_ASSERT(hdr.identifier == id);
|
}
|
TEST_ASSERT(hdr.length == payload_len);
|
|
om->om_data += BLE_L2CAP_SIG_HDR_SZ;
|
om->om_len -= BLE_L2CAP_SIG_HDR_SZ;
|
|
if (out_hdr != NULL) {
|
*out_hdr = hdr;
|
}
|
|
return om;
|
}
|
|
int
|
ble_hs_test_util_inject_rx_l2cap_sig(uint16_t conn_handle, uint8_t opcode,
|
uint8_t id, void *cmd, uint16_t cmd_size)
|
{
|
void *r;
|
struct hci_data_hdr hci_hdr;
|
struct os_mbuf *om;
|
int rc;
|
|
hci_hdr = BLE_HS_TEST_UTIL_L2CAP_HCI_HDR(2, BLE_HCI_PB_FIRST_FLUSH,
|
BLE_L2CAP_HDR_SZ + BLE_L2CAP_SIG_HDR_SZ + cmd_size);
|
|
r = ble_l2cap_sig_cmd_get(opcode, id, cmd_size, &om);
|
TEST_ASSERT_FATAL(r != NULL);
|
|
memcpy(r, cmd, cmd_size);
|
|
rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SIG,
|
&hci_hdr, om);
|
return rc;
|
}
|
|
/**
|
* @return The L2CAP sig identifier in the request/response.
|
*/
|
uint8_t
|
ble_hs_test_util_verify_tx_l2cap_sig(uint16_t opcode, void *cmd,
|
uint16_t cmd_size)
|
{
|
struct ble_l2cap_sig_hdr hdr;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_verify_tx_l2cap_sig_hdr(opcode, 0, cmd_size, &hdr);
|
om = os_mbuf_pullup(om, cmd_size);
|
|
/* Verify payload. */
|
TEST_ASSERT(memcmp(om->om_data, cmd, cmd_size) == 0);
|
|
return hdr.identifier;
|
}
|
|
void
|
ble_hs_test_util_verify_tx_l2cap(struct os_mbuf *txom)
|
{
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_prev_tx_dequeue();
|
TEST_ASSERT_FATAL(om != NULL);
|
|
/* TODO Handle fragmentation */
|
TEST_ASSERT_FATAL(os_mbuf_cmpm(om, 0, txom, 0, OS_MBUF_PKTLEN(om)) == 0);
|
}
|
|
void
|
ble_hs_test_util_inject_rx_l2cap(uint16_t conn_handle, uint16_t cid,
|
struct os_mbuf *rxom)
|
{
|
struct hci_data_hdr hci_hdr;
|
int rc;
|
|
hci_hdr = BLE_HS_TEST_UTIL_L2CAP_HCI_HDR(2, BLE_HCI_PB_FIRST_FLUSH,
|
BLE_L2CAP_HDR_SZ +
|
BLE_L2CAP_SIG_HDR_SZ +
|
OS_MBUF_PKTLEN(rxom));
|
|
rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, cid, &hci_hdr, rxom);
|
TEST_ASSERT(rc == 0);
|
}
|
|
static void
|
ble_l2cap_test_update_req_swap(struct ble_l2cap_sig_update_req *dst,
|
struct ble_l2cap_sig_update_req *src)
|
{
|
dst->itvl_min = le16toh(src->itvl_min);
|
dst->itvl_max = le16toh(src->itvl_max);
|
dst->slave_latency = le16toh(src->slave_latency);
|
dst->timeout_multiplier = le16toh(src->timeout_multiplier);
|
}
|
|
static void
|
ble_l2cap_test_update_req_parse(void *payload, int len,
|
struct ble_l2cap_sig_update_req *dst)
|
{
|
BLE_HS_DBG_ASSERT(len >= BLE_L2CAP_SIG_UPDATE_REQ_SZ);
|
ble_l2cap_test_update_req_swap(dst, payload);
|
}
|
|
/**
|
* @return The L2CAP sig identifier in the request.
|
*/
|
uint8_t
|
ble_hs_test_util_verify_tx_l2cap_update_req(
|
struct ble_l2cap_sig_update_params *params)
|
{
|
struct ble_l2cap_sig_update_req req;
|
struct ble_l2cap_sig_hdr hdr;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_verify_tx_l2cap_sig_hdr(BLE_L2CAP_SIG_OP_UPDATE_REQ,
|
0,
|
BLE_L2CAP_SIG_UPDATE_REQ_SZ,
|
&hdr);
|
|
/* Verify payload. */
|
ble_l2cap_test_update_req_parse(om->om_data, om->om_len, &req);
|
TEST_ASSERT(req.itvl_min == params->itvl_min);
|
TEST_ASSERT(req.itvl_max == params->itvl_max);
|
TEST_ASSERT(req.slave_latency == params->slave_latency);
|
TEST_ASSERT(req.timeout_multiplier == params->timeout_multiplier);
|
|
return hdr.identifier;
|
}
|
|
static void
|
ble_l2cap_sig_update_rsp_parse(void *payload, int len,
|
struct ble_l2cap_sig_update_rsp *dst)
|
{
|
struct ble_l2cap_sig_update_rsp *src = payload;
|
|
BLE_HS_DBG_ASSERT(len >= BLE_L2CAP_SIG_UPDATE_RSP_SZ);
|
dst->result = le16toh(src->result);
|
}
|
|
int
|
ble_hs_test_util_rx_l2cap_update_rsp(uint16_t conn_handle,
|
uint8_t id, uint16_t result)
|
{
|
struct ble_l2cap_sig_update_rsp *rsp;
|
struct hci_data_hdr hci_hdr;
|
struct os_mbuf *om;
|
int rc;
|
|
hci_hdr = BLE_HS_TEST_UTIL_L2CAP_HCI_HDR(
|
2, BLE_HCI_PB_FIRST_FLUSH,
|
BLE_L2CAP_HDR_SZ + BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ);
|
|
rsp = ble_l2cap_sig_cmd_get(BLE_L2CAP_SIG_OP_UPDATE_RSP, id,
|
BLE_L2CAP_SIG_UPDATE_RSP_SZ, &om);
|
TEST_ASSERT_FATAL(rsp != NULL);
|
|
rsp->result = htole16(result);
|
|
rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SIG,
|
&hci_hdr, om);
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_verify_tx_l2cap_update_rsp(uint8_t exp_id,
|
uint16_t exp_result)
|
{
|
struct ble_l2cap_sig_update_rsp rsp;
|
struct os_mbuf *om;
|
|
om = ble_hs_test_util_verify_tx_l2cap_sig_hdr(BLE_L2CAP_SIG_OP_UPDATE_RSP,
|
exp_id,
|
BLE_L2CAP_SIG_UPDATE_RSP_SZ,
|
NULL);
|
|
ble_l2cap_sig_update_rsp_parse(om->om_data, om->om_len, &rsp);
|
TEST_ASSERT(rsp.result == exp_result);
|
}
|
|
void
|
ble_hs_test_util_set_static_rnd_addr(const uint8_t *addr)
|
{
|
int rc;
|
|
ble_hs_test_util_hci_ack_set(
|
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_RAND_ADDR), 0);
|
|
rc = ble_hs_id_set_rnd(addr);
|
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_hs_test_util_hci_out_first();
|
}
|
|
struct os_mbuf *
|
ble_hs_test_util_om_from_flat(const void *buf, uint16_t len)
|
{
|
struct os_mbuf *om;
|
|
om = ble_hs_mbuf_from_flat(buf, len);
|
TEST_ASSERT_FATAL(om != NULL);
|
|
return om;
|
}
|
|
int
|
ble_hs_test_util_flat_attr_cmp(const struct ble_hs_test_util_flat_attr *a,
|
const struct ble_hs_test_util_flat_attr *b)
|
{
|
if (a->handle != b->handle) {
|
return -1;
|
}
|
if (a->offset != b->offset) {
|
return -1;
|
}
|
if (a->value_len != b->value_len) {
|
return -1;
|
}
|
return memcmp(a->value, b->value, a->value_len);
|
}
|
|
void
|
ble_hs_test_util_attr_to_flat(struct ble_hs_test_util_flat_attr *flat,
|
const struct ble_gatt_attr *attr)
|
{
|
int rc;
|
|
flat->handle = attr->handle;
|
flat->offset = attr->offset;
|
rc = ble_hs_mbuf_to_flat(attr->om, flat->value, sizeof flat->value,
|
&flat->value_len);
|
TEST_ASSERT_FATAL(rc == 0);
|
}
|
|
void
|
ble_hs_test_util_attr_from_flat(struct ble_gatt_attr *attr,
|
const struct ble_hs_test_util_flat_attr *flat)
|
{
|
attr->handle = flat->handle;
|
attr->offset = flat->offset;
|
attr->om = ble_hs_test_util_om_from_flat(flat->value, flat->value_len);
|
}
|
|
int
|
ble_hs_test_util_read_local_flat(uint16_t attr_handle, uint16_t max_len,
|
void *buf, uint16_t *out_len)
|
{
|
struct os_mbuf *om;
|
int rc;
|
|
rc = ble_att_svr_read_local(attr_handle, &om);
|
if (rc != 0) {
|
return rc;
|
}
|
|
TEST_ASSERT_FATAL(OS_MBUF_PKTLEN(om) <= max_len);
|
|
rc = os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), buf);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
*out_len = OS_MBUF_PKTLEN(om);
|
|
rc = os_mbuf_free_chain(om);
|
TEST_ASSERT_FATAL(rc == 0);
|
return 0;
|
}
|
|
int
|
ble_hs_test_util_write_local_flat(uint16_t attr_handle,
|
const void *buf, uint16_t buf_len)
|
{
|
struct os_mbuf *om;
|
int rc;
|
|
om = ble_hs_test_util_om_from_flat(buf, buf_len);
|
rc = ble_att_svr_write_local(attr_handle, om);
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_gatt_write_flat(uint16_t conn_handle, uint16_t attr_handle,
|
const void *data, uint16_t data_len,
|
ble_gatt_attr_fn *cb, void *cb_arg)
|
{
|
struct os_mbuf *om;
|
int rc;
|
|
om = ble_hs_test_util_om_from_flat(data, data_len);
|
rc = ble_gattc_write(conn_handle, attr_handle, om, cb, cb_arg);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_gatt_write_no_rsp_flat(uint16_t conn_handle,
|
uint16_t attr_handle,
|
const void *data, uint16_t data_len)
|
{
|
struct os_mbuf *om;
|
int rc;
|
|
om = ble_hs_test_util_om_from_flat(data, data_len);
|
rc = ble_gattc_write_no_rsp(conn_handle, attr_handle, om);
|
|
return rc;
|
}
|
|
int
|
ble_hs_test_util_gatt_write_long_flat(uint16_t conn_handle,
|
uint16_t attr_handle,
|
const void *data, uint16_t data_len,
|
ble_gatt_attr_fn *cb, void *cb_arg)
|
{
|
struct os_mbuf *om;
|
uint16_t offset = 0;
|
int rc;
|
|
om = ble_hs_test_util_om_from_flat(data, data_len);
|
rc = ble_gattc_write_long(conn_handle, attr_handle, offset, om, cb, cb_arg);
|
|
return rc;
|
}
|
|
static int
|
ble_hs_test_util_mbuf_chain_len(const struct os_mbuf *om)
|
{
|
int count;
|
|
count = 0;
|
while (om != NULL) {
|
count++;
|
om = SLIST_NEXT(om, om_next);
|
}
|
|
return count;
|
}
|
|
static int
|
ble_hs_test_util_num_mbufs(void)
|
{
|
return os_msys_count() + ble_hs_hci_frag_num_mbufs();
|
}
|
|
static int
|
ble_hs_test_util_num_mbufs_free(void)
|
{
|
return os_msys_num_free() + ble_hs_hci_frag_num_mbufs_free();
|
}
|
|
struct os_mbuf *
|
ble_hs_test_util_mbuf_alloc_all_but(int count)
|
{
|
struct os_mbuf *prev;
|
struct os_mbuf *om;
|
int rc;
|
int i;
|
|
/* Allocate all available mbufs and put them in a single chain. */
|
prev = NULL;
|
while (1) {
|
om = os_msys_get(0, 0);
|
if (om == NULL) {
|
break;
|
}
|
|
if (prev != NULL) {
|
SLIST_NEXT(om, om_next) = prev;
|
}
|
|
prev = om;
|
}
|
|
/* Now free 'count' mbufs. */
|
for (i = 0; i < count; i++) {
|
TEST_ASSERT_FATAL(prev != NULL);
|
om = SLIST_NEXT(prev, om_next);
|
rc = os_mbuf_free(prev);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
prev = om;
|
}
|
|
return prev;
|
}
|
|
int
|
ble_hs_test_util_mbuf_count(const struct ble_hs_test_util_mbuf_params *params)
|
{
|
const struct ble_att_prep_entry *prep;
|
const struct os_mbuf_pkthdr *omp;
|
const struct ble_l2cap_chan *chan;
|
const struct ble_hs_conn *conn;
|
const struct os_mbuf *om;
|
int count;
|
int i;
|
|
ble_hs_process_rx_data_queue();
|
|
count = ble_hs_test_util_num_mbufs_free();
|
|
if (params->prev_tx) {
|
count += ble_hs_test_util_mbuf_chain_len(ble_hs_test_util_prev_tx_cur);
|
STAILQ_FOREACH(omp, &ble_hs_test_util_prev_tx_queue, omp_next) {
|
om = OS_MBUF_PKTHDR_TO_MBUF(omp);
|
count += ble_hs_test_util_mbuf_chain_len(om);
|
}
|
}
|
|
ble_hs_lock();
|
for (i = 0; ; i++) {
|
conn = ble_hs_conn_find_by_idx(i);
|
if (conn == NULL) {
|
break;
|
}
|
|
if (params->rx_queue) {
|
SLIST_FOREACH(chan, &conn->bhc_channels, next) {
|
count += ble_hs_test_util_mbuf_chain_len(chan->rx_buf);
|
}
|
}
|
|
if (params->prep_list) {
|
SLIST_FOREACH(prep, &conn->bhc_att_svr.basc_prep_list, bape_next) {
|
count += ble_hs_test_util_mbuf_chain_len(prep->bape_value);
|
}
|
}
|
}
|
ble_hs_unlock();
|
|
return count;
|
}
|
|
void
|
ble_hs_test_util_assert_mbufs_freed(
|
const struct ble_hs_test_util_mbuf_params *params)
|
{
|
static const struct ble_hs_test_util_mbuf_params dflt = {
|
.prev_tx = 1,
|
.rx_queue = 1,
|
.prep_list = 1,
|
};
|
|
int count;
|
|
if (params == NULL) {
|
params = &dflt;
|
}
|
|
count = ble_hs_test_util_mbuf_count(params);
|
TEST_ASSERT(count == ble_hs_test_util_num_mbufs());
|
}
|
|
int
|
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
|
{
|
ble_hs_test_util_prev_tx_enqueue(om);
|
return 0;
|
}
|
|
int
|
ble_transport_to_ll_cmd_impl(void *buf)
|
{
|
ble_hs_test_util_hci_out_enqueue(buf);
|
ble_transport_free(buf);
|
return 0;
|
}
|
|
int
|
ble_hs_test_util_num_cccds(void)
|
{
|
struct ble_store_value_cccd val;
|
struct ble_store_key_cccd key = { };
|
int rc;
|
|
key.peer_addr = *BLE_ADDR_ANY;
|
for (key.idx = 0; ; key.idx++) {
|
rc = ble_store_read_cccd(&key, &val);
|
switch (rc) {
|
case 0:
|
break;
|
|
case BLE_HS_ENOENT:
|
return key.idx;
|
|
default:
|
TEST_ASSERT_FATAL(0);
|
}
|
}
|
}
|
|
int
|
ble_hs_test_util_num_our_secs(void)
|
{
|
struct ble_store_value_sec val;
|
struct ble_store_key_sec key = { };
|
int rc;
|
|
key.peer_addr = *BLE_ADDR_ANY;
|
for (key.idx = 0; ; key.idx++) {
|
rc = ble_store_read_our_sec(&key, &val);
|
switch (rc) {
|
case 0:
|
break;
|
|
case BLE_HS_ENOENT:
|
return key.idx;
|
|
default:
|
TEST_ASSERT_FATAL(0);
|
}
|
}
|
}
|
|
int
|
ble_hs_test_util_num_peer_secs(void)
|
{
|
struct ble_store_value_sec val;
|
struct ble_store_key_sec key = { };
|
int rc;
|
|
key.peer_addr = *BLE_ADDR_ANY;
|
for (key.idx = 0; ; key.idx++) {
|
rc = ble_store_read_peer_sec(&key, &val);
|
switch (rc) {
|
case 0:
|
break;
|
|
case BLE_HS_ENOENT:
|
return key.idx;
|
|
default:
|
TEST_ASSERT_FATAL(0);
|
}
|
}
|
}
|
|
static int
|
ble_hs_test_util_store_read(int obj_type, const union ble_store_key *key,
|
union ble_store_value *value)
|
{
|
int rc;
|
|
ble_sm_test_store_obj_type = obj_type;
|
ble_sm_test_store_key = *key;
|
|
rc = ble_store_config_read(obj_type, key, value);
|
ble_sm_test_store_value = *value;
|
|
return rc;
|
}
|
|
static int
|
ble_hs_test_util_store_write(int obj_type, const union ble_store_value *value)
|
{
|
int rc;
|
|
ble_sm_test_store_obj_type = obj_type;
|
|
rc = ble_store_config_write(obj_type, value);
|
ble_sm_test_store_value = *value;
|
|
return rc;
|
}
|
|
static int
|
ble_hs_test_util_store_delete(int obj_type, const union ble_store_key *key)
|
{
|
int rc;
|
|
ble_sm_test_store_obj_type = obj_type;
|
ble_sm_test_store_key = *key;
|
|
rc = ble_store_config_delete(obj_type, key);
|
return rc;
|
}
|
|
void
|
ble_hs_test_util_reg_svcs(const struct ble_gatt_svc_def *svcs,
|
ble_gatt_register_fn *reg_cb,
|
void *cb_arg)
|
{
|
int rc;
|
|
ble_hs_cfg.gatts_register_cb = reg_cb;
|
ble_hs_cfg.gatts_register_arg = cb_arg;
|
|
rc = ble_gatts_reset();
|
TEST_ASSERT_FATAL(rc == 0);
|
|
rc = ble_gatts_add_svcs(svcs);
|
TEST_ASSERT_FATAL(rc == 0);
|
|
rc = ble_gatts_start();
|
TEST_ASSERT_FATAL(rc == 0);
|
}
|
|
|
void
|
ble_hs_test_util_init_no_sysinit_no_start(void)
|
{
|
STAILQ_INIT(&ble_hs_test_util_prev_tx_queue);
|
ble_hs_test_util_prev_tx_cur = NULL;
|
|
ble_hs_hci_set_phony_ack_cb(NULL);
|
|
ble_hs_test_util_hci_ack_set_startup();
|
|
ble_hs_enabled_state = BLE_HS_ENABLED_STATE_OFF;
|
|
ble_hs_max_services = 16;
|
ble_hs_max_client_configs = 32;
|
ble_hs_max_attrs = 64;
|
|
ble_hs_test_util_hci_out_clear();
|
|
ble_hs_cfg.store_read_cb = ble_hs_test_util_store_read;
|
ble_hs_cfg.store_write_cb = ble_hs_test_util_store_write;
|
ble_hs_cfg.store_delete_cb = ble_hs_test_util_store_delete;
|
|
ble_store_clear();
|
}
|
|
void
|
ble_hs_test_util_init_no_start(void)
|
{
|
sysinit();
|
ble_hs_test_util_init_no_sysinit_no_start();
|
}
|
|
void
|
ble_hs_test_util_init(void)
|
{
|
int rc;
|
|
ble_hs_test_util_init_no_start();
|
|
rc = ble_hs_start();
|
TEST_ASSERT_FATAL(rc == 0);
|
|
ble_hs_test_util_hci_out_clear();
|
ble_hs_test_util_hci_acks_clear();
|
|
/* Clear random address. */
|
ble_hs_id_rnd_reset();
|
}
|
|
void
|
ble_transport_ll_init(void)
|
{
|
/* nothing here */
|
}
|