You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/09/13 03:42:01 UTC
[08/41] incubator-mynewt-core git commit: syscfg / sysinit
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_hs_test_util.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_hs_test_util.c b/net/nimble/host/test/src/ble_hs_test_util.c
new file mode 100644
index 0000000..eed1262
--- /dev/null
+++ b/net/nimble/host/test/src/ble_hs_test_util.c
@@ -0,0 +1,1404 @@
+/**
+ * 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 "stats/stats.h"
+#include "testutil/testutil.h"
+#include "nimble/ble.h"
+#include "nimble/hci_common.h"
+#include "nimble/ble_hci_trans.h"
+#include "host/ble_hs_adv.h"
+#include "host/ble_hs_id.h"
+#include "transport/ram/ble_hci_ram.h"
+#include "ble_hs_test_util.h"
+
+/* Our global device address. */
+uint8_t g_dev_addr[BLE_DEV_ADDR_LEN];
+
+#define BLE_HS_TEST_UTIL_PUB_ADDR_VAL { 0x0a, 0x54, 0xab, 0x49, 0x7f, 0x06 }
+
+static const uint8_t ble_hs_test_util_pub_addr[BLE_DEV_ADDR_LEN] =
+ BLE_HS_TEST_UTIL_PUB_ADDR_VAL;
+
+#define BLE_HS_TEST_UTIL_LE_OPCODE(ocf) \
+ ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, (ocf))
+
+struct os_eventq ble_hs_test_util_evq;
+
+static STAILQ_HEAD(, os_mbuf_pkthdr) ble_hs_test_util_prev_tx_queue;
+struct os_mbuf *ble_hs_test_util_prev_tx_cur;
+
+#define BLE_HS_TEST_UTIL_PREV_HCI_TX_CNT 64
+static uint8_t
+ble_hs_test_util_prev_hci_tx[BLE_HS_TEST_UTIL_PREV_HCI_TX_CNT][260];
+int ble_hs_test_util_num_prev_hci_txes;
+
+uint8_t ble_hs_test_util_cur_hci_tx[260];
+
+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;
+
+ os_mbuf_free_chain(ble_hs_test_util_prev_tx_cur);
+
+ 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.blh_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)
+{
+ ble_hs_test_util_tx_all();
+ while (!STAILQ_EMPTY(&ble_hs_test_util_prev_tx_queue)) {
+ ble_hs_test_util_prev_tx_dequeue();
+ }
+}
+
+void *
+ble_hs_test_util_get_first_hci_tx(void)
+{
+ if (ble_hs_test_util_num_prev_hci_txes == 0) {
+ return NULL;
+ }
+
+ memcpy(ble_hs_test_util_cur_hci_tx, ble_hs_test_util_prev_hci_tx[0],
+ sizeof ble_hs_test_util_cur_hci_tx);
+
+ ble_hs_test_util_num_prev_hci_txes--;
+ if (ble_hs_test_util_num_prev_hci_txes > 0) {
+ memmove(
+ ble_hs_test_util_prev_hci_tx, ble_hs_test_util_prev_hci_tx + 1,
+ sizeof ble_hs_test_util_prev_hci_tx[0] *
+ ble_hs_test_util_num_prev_hci_txes);
+ }
+
+ return ble_hs_test_util_cur_hci_tx;
+}
+
+void *
+ble_hs_test_util_get_last_hci_tx(void)
+{
+ if (ble_hs_test_util_num_prev_hci_txes == 0) {
+ return NULL;
+ }
+
+ ble_hs_test_util_num_prev_hci_txes--;
+ memcpy(ble_hs_test_util_cur_hci_tx,
+ ble_hs_test_util_prev_hci_tx + ble_hs_test_util_num_prev_hci_txes,
+ sizeof ble_hs_test_util_cur_hci_tx);
+
+ return ble_hs_test_util_cur_hci_tx;
+}
+
+void
+ble_hs_test_util_enqueue_hci_tx(void *cmd)
+{
+ TEST_ASSERT_FATAL(ble_hs_test_util_num_prev_hci_txes <
+ BLE_HS_TEST_UTIL_PREV_HCI_TX_CNT);
+ memcpy(ble_hs_test_util_prev_hci_tx + ble_hs_test_util_num_prev_hci_txes,
+ cmd, 260);
+
+ ble_hs_test_util_num_prev_hci_txes++;
+}
+
+void
+ble_hs_test_util_prev_hci_tx_clear(void)
+{
+ ble_hs_test_util_num_prev_hci_txes = 0;
+}
+
+static void
+ble_hs_test_util_rx_hci_evt(uint8_t *evt)
+{
+ uint8_t *evbuf;
+ int totlen;
+ int rc;
+
+ totlen = BLE_HCI_EVENT_HDR_LEN + evt[1];
+ TEST_ASSERT_FATAL(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
+
+ if (os_started()) {
+ evbuf = ble_hci_trans_buf_alloc(
+ BLE_HCI_TRANS_BUF_EVT_LO);
+ TEST_ASSERT_FATAL(evbuf != NULL);
+
+ memcpy(evbuf, evt, totlen);
+ rc = ble_hci_trans_ll_evt_tx(evbuf);
+ } else {
+ rc = ble_hs_hci_evt_process(evt);
+ }
+
+ TEST_ASSERT_FATAL(rc == 0);
+}
+
+void
+ble_hs_test_util_build_cmd_complete(uint8_t *dst, int len,
+ uint8_t param_len, uint8_t num_pkts,
+ uint16_t opcode)
+{
+ TEST_ASSERT(len >= BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN);
+
+ dst[0] = BLE_HCI_EVCODE_COMMAND_COMPLETE;
+ dst[1] = 3 + param_len;
+ dst[2] = num_pkts;
+ htole16(dst + 3, opcode);
+}
+
+void
+ble_hs_test_util_build_cmd_status(uint8_t *dst, int len,
+ uint8_t status, uint8_t num_pkts,
+ uint16_t opcode)
+{
+ TEST_ASSERT(len >= BLE_HCI_EVENT_CMD_STATUS_LEN);
+
+ dst[0] = BLE_HCI_EVCODE_COMMAND_STATUS;
+ dst[1] = BLE_HCI_EVENT_CMD_STATUS_LEN;
+ dst[2] = status;
+ dst[3] = num_pkts;
+ htole16(dst + 4, opcode);
+}
+
+#define BLE_HS_TEST_UTIL_PHONY_ACK_MAX 64
+struct ble_hs_test_util_phony_ack {
+ uint16_t opcode;
+ uint8_t status;
+ uint8_t evt_params[256];
+ uint8_t evt_params_len;
+};
+
+static struct ble_hs_test_util_phony_ack
+ble_hs_test_util_phony_acks[BLE_HS_TEST_UTIL_PHONY_ACK_MAX];
+static int ble_hs_test_util_num_phony_acks;
+
+static int
+ble_hs_test_util_phony_ack_cb(uint8_t *ack, int ack_buf_len)
+{
+ struct ble_hs_test_util_phony_ack *entry;
+
+ if (ble_hs_test_util_num_phony_acks == 0) {
+ return BLE_HS_ETIMEOUT_HCI;
+ }
+
+ entry = ble_hs_test_util_phony_acks;
+
+ ble_hs_test_util_build_cmd_complete(ack, 256,
+ entry->evt_params_len + 1, 1,
+ entry->opcode);
+ ack[BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN] = entry->status;
+ memcpy(ack + BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN + 1, entry->evt_params,
+ entry->evt_params_len);
+
+ ble_hs_test_util_num_phony_acks--;
+ if (ble_hs_test_util_num_phony_acks > 0) {
+ memmove(ble_hs_test_util_phony_acks, ble_hs_test_util_phony_acks + 1,
+ sizeof *entry * ble_hs_test_util_num_phony_acks);
+ }
+
+ return 0;
+}
+
+void
+ble_hs_test_util_set_ack_params(uint16_t opcode, uint8_t status, void *params,
+ uint8_t params_len)
+{
+ struct ble_hs_test_util_phony_ack *ack;
+
+ ack = ble_hs_test_util_phony_acks + 0;
+ ack->opcode = opcode;
+ ack->status = status;
+
+ if (params == NULL || params_len == 0) {
+ ack->evt_params_len = 0;
+ } else {
+ memcpy(ack->evt_params, params, params_len);
+ ack->evt_params_len = params_len;
+ }
+ ble_hs_test_util_num_phony_acks = 1;
+
+ ble_hs_hci_set_phony_ack_cb(ble_hs_test_util_phony_ack_cb);
+}
+
+void
+ble_hs_test_util_set_ack(uint16_t opcode, uint8_t status)
+{
+ ble_hs_test_util_set_ack_params(opcode, status, NULL, 0);
+}
+
+static void
+ble_hs_test_util_set_ack_seq(struct ble_hs_test_util_phony_ack *acks)
+{
+ int i;
+
+ for (i = 0; acks[i].opcode != 0; i++) {
+ ble_hs_test_util_phony_acks[i] = acks[i];
+ }
+ ble_hs_test_util_num_phony_acks = i;
+
+ ble_hs_hci_set_phony_ack_cb(ble_hs_test_util_phony_ack_cb);
+}
+
+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,
+ ble_gap_event_fn *cb, void *cb_arg)
+{
+ struct hci_le_conn_complete evt;
+ int rc;
+
+ ble_hs_test_util_connect(own_addr_type, peer_addr_type,
+ peer_id_addr, 0, NULL, cb, cb_arg, 0);
+
+ memset(&evt, 0, sizeof evt);
+ evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;
+ 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);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_test_util_prev_hci_tx_clear();
+}
+
+void
+ble_hs_test_util_create_conn(uint16_t handle, 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_ADDR_TYPE_PUBLIC, null_addr,
+ BLE_ADDR_TYPE_PUBLIC, peer_id_addr,
+ null_addr, cb, cb_arg);
+}
+
+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, uint8_t peer_addr_type,
+ const uint8_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_type == BLE_GAP_ADDR_TYPE_WL) {
+ 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, 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_verify_tx_create_conn(const struct hci_create_conn *exp)
+{
+ uint8_t param_len;
+ uint8_t *param;
+
+ param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE,
+ BLE_HCI_OCF_LE_CREATE_CONN,
+ ¶m_len);
+ TEST_ASSERT(param_len == BLE_HCI_CREATE_CONN_LEN);
+
+ TEST_ASSERT(le16toh(param + 0) == exp->scan_itvl);
+ TEST_ASSERT(le16toh(param + 2) == exp->scan_window);
+ TEST_ASSERT(param[4] == exp->filter_policy);
+ TEST_ASSERT(param[5] == exp->peer_addr_type);
+ TEST_ASSERT(memcmp(param + 6, exp->peer_addr, 6) == 0);
+ TEST_ASSERT(param[12] == exp->own_addr_type);
+ TEST_ASSERT(le16toh(param + 13) == exp->conn_itvl_min);
+ TEST_ASSERT(le16toh(param + 15) == exp->conn_itvl_max);
+ TEST_ASSERT(le16toh(param + 17) == exp->conn_latency);
+ TEST_ASSERT(le16toh(param + 19) == exp->supervision_timeout);
+ TEST_ASSERT(le16toh(param + 21) == exp->min_ce_len);
+ TEST_ASSERT(le16toh(param + 23) == exp->max_ce_len);
+}
+
+int
+ble_hs_test_util_connect(uint8_t own_addr_type, uint8_t peer_addr_type,
+ const uint8_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_prev_hci_tx_clear();
+
+ ble_hs_test_util_set_ack(
+ 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_type, peer_addr, duration_ms,
+ params, cb, cb_arg);
+
+ TEST_ASSERT(rc == BLE_HS_HCI_ERR(ack_status));
+
+ 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_type, peer_addr, params);
+ ble_hs_test_util_verify_tx_create_conn(&hcc);
+
+ return rc;
+}
+
+int
+ble_hs_test_util_conn_cancel(uint8_t ack_status)
+{
+ int rc;
+
+ ble_hs_test_util_set_ack(
+ 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_conn_cancel_full(void)
+{
+ struct hci_le_conn_complete evt;
+ int rc;
+
+ ble_hs_test_util_conn_cancel(0);
+
+ memset(&evt, 0, sizeof evt);
+ evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;
+ evt.status = BLE_ERR_UNK_CONN_ID;
+ evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER;
+
+ rc = ble_gap_rx_conn_complete(&evt);
+ TEST_ASSERT_FATAL(rc == 0);
+}
+
+int
+ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status)
+{
+ int rc;
+
+ ble_hs_test_util_set_ack(
+ ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LINK_CTRL,
+ BLE_HCI_OCF_DISCONNECT_CMD),
+ 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)
+{
+ struct hci_disconn_complete evt;
+ int rc;
+
+ rc = ble_hs_test_util_conn_terminate(conn_handle, 0);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ /* Receive disconnection complete event. */
+ evt.connection_handle = conn_handle;
+ evt.status = 0;
+ evt.reason = BLE_ERR_CONN_TERM_LOCAL;
+ ble_gap_rx_disconn_complete(&evt);
+}
+
+int
+ble_hs_test_util_exp_hci_status(int cmd_idx, int fail_idx, uint8_t fail_status)
+{
+ if (cmd_idx == fail_idx) {
+ return BLE_HS_HCI_ERR(fail_status);
+ } else {
+ return 0;
+ }
+}
+
+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_set_ack_seq(((struct ble_hs_test_util_phony_ack[]) {
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_PARAMS),
+ ble_hs_test_util_exp_hci_status(0, fail_idx, fail_status),
+ },
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
+ ble_hs_test_util_exp_hci_status(1, fail_idx, fail_status),
+ },
+
+ { 0 }
+ }));
+
+ 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_set_ack(
+ 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_verify_tx_hci(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(struct ble_hs_adv_fields *adv_fields,
+ uint8_t hci_status)
+{
+ int auto_pwr;
+ int rc;
+
+ auto_pwr = adv_fields->tx_pwr_lvl_is_present &&
+ adv_fields->tx_pwr_lvl == BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+ if (auto_pwr) {
+ ble_hs_test_util_set_ack_params(
+ ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
+ BLE_HCI_OCF_LE_RD_ADV_CHAN_TXPWR),
+ hci_status,
+ ((uint8_t[1]){0}), 1);
+ }
+
+ 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_start(uint8_t own_addr_type,
+ uint8_t peer_addr_type, const uint8_t *peer_addr,
+ const struct ble_gap_adv_params *adv_params,
+ ble_gap_event_fn *cb, void *cb_arg,
+ int fail_idx, uint8_t fail_status)
+{
+ struct ble_hs_test_util_phony_ack acks[6];
+ int rc;
+ int i;
+
+ i = 0;
+
+ acks[i] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_PARAMS),
+ fail_idx == i ? fail_status : 0,
+ };
+ i++;
+
+ if (adv_params->conn_mode != BLE_GAP_CONN_MODE_DIR) {
+ acks[i] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_DATA),
+ ble_hs_test_util_exp_hci_status(i, fail_idx, fail_status),
+ };
+ i++;
+
+ acks[i] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_RSP_DATA),
+ ble_hs_test_util_exp_hci_status(i, fail_idx, fail_status),
+ };
+ i++;
+ }
+
+ acks[i] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
+ ble_hs_test_util_exp_hci_status(i, fail_idx, fail_status),
+ };
+ i++;
+
+ memset(acks + i, 0, sizeof acks[i]);
+
+ ble_hs_test_util_set_ack_seq(acks);
+
+ rc = ble_gap_adv_start(own_addr_type, peer_addr_type, peer_addr,
+ BLE_HS_FOREVER, adv_params, cb, cb_arg);
+
+ return rc;
+}
+
+int
+ble_hs_test_util_adv_stop(uint8_t hci_status)
+{
+ int rc;
+
+ ble_hs_test_util_set_ack(
+ 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(struct ble_gap_white_entry *white_list,
+ uint8_t white_list_count,
+ int fail_idx, uint8_t fail_status)
+{
+ struct ble_hs_test_util_phony_ack acks[64];
+ int cmd_idx;
+ int rc;
+ int i;
+
+ TEST_ASSERT_FATAL(white_list_count < 63);
+
+ cmd_idx = 0;
+ acks[cmd_idx] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CLEAR_WHITE_LIST),
+ ble_hs_test_util_exp_hci_status(cmd_idx, fail_idx, fail_status),
+ };
+ cmd_idx++;
+
+ for (i = 0; i < white_list_count; i++) {
+ acks[cmd_idx] = (struct ble_hs_test_util_phony_ack) {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_ADD_WHITE_LIST),
+ ble_hs_test_util_exp_hci_status(cmd_idx, fail_idx, fail_status),
+ };
+
+ cmd_idx++;
+ }
+ memset(acks + cmd_idx, 0, sizeof acks[cmd_idx]);
+
+ ble_hs_test_util_set_ack_seq(acks);
+ rc = ble_gap_wl_set(white_list, white_list_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;
+
+ ble_hs_test_util_set_ack(
+ 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_set_ack_seq(((struct ble_hs_test_util_phony_ack[]) {
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
+ ble_hs_test_util_exp_hci_status(0, fail_idx, hci_status),
+ },
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CLR_RESOLV_LIST),
+ ble_hs_test_util_exp_hci_status(1, fail_idx, hci_status),
+ },
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
+ ble_hs_test_util_exp_hci_status(2, fail_idx, hci_status),
+ },
+ {
+ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
+ ble_hs_test_util_exp_hci_status(3, 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_set_ack(
+ 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;
+ struct os_mbuf *rx_buf;
+ 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, &rx_buf);
+ } else {
+ os_mbuf_free_chain(om);
+ }
+
+ ble_hs_unlock();
+
+ if (conn == NULL) {
+ rc = BLE_HS_ENOTCONN;
+ } else if (rc == 0) {
+ TEST_ASSERT_FATAL(rx_cb != NULL);
+ TEST_ASSERT_FATAL(rx_buf != NULL);
+ rc = rx_cb(conn_handle, &rx_buf);
+ os_mbuf_free_chain(rx_buf);
+ } else if (rc == BLE_HS_EAGAIN) {
+ /* More fragments on the way. */
+ rc = 0;
+ }
+
+ 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_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_set_startup_acks(void)
+{
+ /* Receive acknowledgements for the startup sequence. We sent the
+ * corresponding requests when the host task was started.
+ */
+ ble_hs_test_util_set_ack_seq(((struct ble_hs_test_util_phony_ack[]) {
+ {
+ .opcode = ble_hs_hci_util_opcode_join(BLE_HCI_OGF_CTLR_BASEBAND,
+ BLE_HCI_OCF_CB_RESET),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_SET_EVENT_MASK),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_SET_EVENT_MASK2),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_EVENT_MASK),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_BUF_SIZE),
+ /* Use a very low buffer size (16) to test fragmentation. */
+ .evt_params = { 0x10, 0x00, 0x20 },
+ .evt_params_len = 3,
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT),
+ .evt_params = { 0 },
+ .evt_params_len = 8,
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_INFO_PARAMS, BLE_HCI_OCF_IP_RD_BD_ADDR),
+ .evt_params = BLE_HS_TEST_UTIL_PUB_ADDR_VAL,
+ .evt_params_len = 6,
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLR_RESOLV_LIST),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADDR_RES_EN),
+ },
+ {
+ .opcode = ble_hs_hci_util_opcode_join(
+ BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
+ },
+ { 0 }
+ }));
+}
+
+void
+ble_hs_test_util_rx_num_completed_pkts_event(
+ struct ble_hs_test_util_num_completed_pkts_entry *entries)
+{
+ struct ble_hs_test_util_num_completed_pkts_entry *entry;
+ uint8_t buf[1024];
+ int num_entries;
+ int off;
+ int i;
+
+ /* Count number of entries. */
+ num_entries = 0;
+ for (entry = entries; entry->handle_id != 0; entry++) {
+ num_entries++;
+ }
+ TEST_ASSERT_FATAL(num_entries <= UINT8_MAX);
+
+ buf[0] = BLE_HCI_EVCODE_NUM_COMP_PKTS;
+ buf[2] = num_entries;
+
+ off = 3;
+ for (i = 0; i < num_entries; i++) {
+ htole16(buf + off, entries[i].handle_id);
+ off += 2;
+ }
+ for (i = 0; i < num_entries; i++) {
+ htole16(buf + off, entries[i].num_pkts);
+ off += 2;
+ }
+
+ buf[1] = off - 2;
+
+ ble_hs_test_util_rx_hci_evt(buf);
+}
+
+void
+ble_hs_test_util_rx_disconn_complete_event(struct hci_disconn_complete *evt)
+{
+ uint8_t buf[BLE_HCI_EVENT_HDR_LEN + BLE_HCI_EVENT_DISCONN_COMPLETE_LEN];
+
+ buf[0] = BLE_HCI_EVCODE_DISCONN_CMP;
+ buf[1] = BLE_HCI_EVENT_DISCONN_COMPLETE_LEN;
+ buf[2] = evt->status;
+ htole16(buf + 3, evt->connection_handle);
+ buf[5] = evt->reason;
+
+ ble_hs_test_util_rx_hci_evt(buf);
+}
+
+uint8_t *
+ble_hs_test_util_verify_tx_hci(uint8_t ogf, uint16_t ocf,
+ uint8_t *out_param_len)
+{
+ uint16_t opcode;
+ uint8_t *cmd;
+
+ cmd = ble_hs_test_util_get_first_hci_tx();
+ TEST_ASSERT_FATAL(cmd != NULL);
+
+ opcode = le16toh(cmd);
+ TEST_ASSERT(BLE_HCI_OGF(opcode) == ogf);
+ TEST_ASSERT(BLE_HCI_OCF(opcode) == ocf);
+
+ if (out_param_len != NULL) {
+ *out_param_len = cmd[2];
+ }
+
+ return cmd + 3;
+}
+
+void
+ble_hs_test_util_tx_all(void)
+{
+ ble_hs_process_tx_data_queue();
+}
+
+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;
+
+ ble_hs_test_util_tx_all();
+ 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;
+
+ ble_hs_test_util_tx_all();
+ 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_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;
+
+ ble_hs_test_util_tx_all();
+
+ 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_set_static_rnd_addr(void)
+{
+ uint8_t addr[6] = { 1, 2, 3, 4, 5, 0xc1 };
+ int rc;
+
+ ble_hs_test_util_set_ack(
+ 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_get_first_hci_tx();
+}
+
+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);
+
+ os_mbuf_free_chain(om);
+ 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;
+ int rc;
+
+ om = ble_hs_test_util_om_from_flat(data, data_len);
+ rc = ble_gattc_write_long(conn_handle, attr_handle, 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;
+}
+
+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_tx_data_queue();
+ ble_hs_process_rx_data_queue();
+
+ count = os_msys_num_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, blc_next) {
+ count += ble_hs_test_util_mbuf_chain_len(chan->blc_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 == os_msys_count());
+}
+
+void
+ble_hs_test_util_post_test(void *arg)
+{
+ ble_hs_test_util_assert_mbufs_freed(arg);
+}
+
+static int
+ble_hs_test_util_pkt_txed(struct os_mbuf *om, void *arg)
+{
+ ble_hs_test_util_prev_tx_enqueue(om);
+ return 0;
+}
+
+static int
+ble_hs_test_util_hci_txed(uint8_t *cmdbuf, void *arg)
+{
+ ble_hs_test_util_enqueue_hci_tx(cmdbuf);
+ ble_hci_trans_buf_free(cmdbuf);
+ return 0;
+}
+
+void
+ble_hs_test_util_init_no_start(void)
+{
+ ble_hs_cfg.parent_evq = &ble_hs_test_util_evq;
+
+ tu_init();
+
+ os_eventq_init(&ble_hs_test_util_evq);
+ 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_hci_trans_cfg_ll(ble_hs_test_util_hci_txed, NULL,
+ ble_hs_test_util_pkt_txed, NULL);
+
+ ble_hs_test_util_set_startup_acks();
+
+ ble_hs_max_services = 16;
+ ble_hs_max_client_configs = 32;
+ ble_hs_max_attrs = 64;
+
+ ble_hs_test_util_prev_hci_tx_clear();
+}
+
+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_prev_hci_tx_clear();
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_hs_test_util.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_hs_test_util.h b/net/nimble/host/test/src/ble_hs_test_util.h
new file mode 100644
index 0000000..5a821cd
--- /dev/null
+++ b/net/nimble/host/test/src/ble_hs_test_util.h
@@ -0,0 +1,177 @@
+/**
+ * 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.
+ */
+
+#ifndef H_BLE_HS_TEST_UTIL_
+#define H_BLE_HS_TEST_UTIL_
+
+#include <inttypes.h>
+#include "host/ble_gap.h"
+#include "ble_hs_priv.h"
+#include "ble_hs_test_util_store.h"
+struct ble_hs_conn;
+struct ble_l2cap_chan;
+struct hci_disconn_complete;
+struct hci_create_conn;
+
+extern struct os_eventq ble_hs_test_util_evq;
+extern const struct ble_gap_adv_params ble_hs_test_util_adv_params;
+
+struct ble_hs_test_util_num_completed_pkts_entry {
+ uint16_t handle_id; /* 0 for terminating entry in array. */
+ uint16_t num_pkts;
+};
+
+struct ble_hs_test_util_flat_attr {
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t value[BLE_ATT_ATTR_MAX_LEN];
+ uint16_t value_len;
+};
+
+struct ble_hs_test_util_mbuf_params {
+ unsigned prev_tx:1;
+ unsigned rx_queue:1;
+ unsigned prep_list:1;
+};
+
+void ble_hs_test_util_prev_tx_enqueue(struct os_mbuf *om);
+struct os_mbuf *ble_hs_test_util_prev_tx_dequeue(void);
+struct os_mbuf *ble_hs_test_util_prev_tx_dequeue_pullup(void);
+int ble_hs_test_util_prev_tx_queue_sz(void);
+void ble_hs_test_util_prev_tx_queue_clear(void);
+
+void ble_hs_test_util_set_ack_params(uint16_t opcode, uint8_t status,
+ void *params, uint8_t params_len);
+void ble_hs_test_util_set_ack(uint16_t opcode, uint8_t status);
+void *ble_hs_test_util_get_first_hci_tx(void);
+void *ble_hs_test_util_get_last_hci_tx(void);
+void ble_hs_test_util_enqueue_hci_tx(void *cmd);
+void ble_hs_test_util_prev_hci_tx_clear(void);
+void ble_hs_test_util_build_cmd_complete(uint8_t *dst, int len,
+ uint8_t param_len, uint8_t num_pkts,
+ uint16_t opcode);
+void ble_hs_test_util_build_cmd_status(uint8_t *dst, int len,
+ uint8_t status, uint8_t num_pkts,
+ uint16_t opcode);
+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,
+ ble_gap_event_fn *cb, void *cb_arg);
+void ble_hs_test_util_create_conn(uint16_t handle, uint8_t *addr,
+ ble_gap_event_fn *cb, void *cb_arg);
+int ble_hs_test_util_connect(uint8_t own_addr_type,
+ uint8_t peer_addr_type,
+ const uint8_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);
+int ble_hs_test_util_conn_cancel(uint8_t ack_status);
+void ble_hs_test_util_conn_cancel_full(void);
+int ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status);
+void ble_hs_test_util_conn_disconnect(uint16_t conn_handle);
+int ble_hs_test_util_exp_hci_status(int cmd_idx, int fail_idx,
+ uint8_t fail_status);
+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 ble_hs_test_util_disc_cancel(uint8_t ack_status);
+void ble_hs_test_util_verify_tx_create_conn(const struct hci_create_conn *exp);
+int ble_hs_test_util_adv_set_fields(struct ble_hs_adv_fields *adv_fields,
+ uint8_t hci_status);
+int ble_hs_test_util_adv_start(uint8_t own_addr_type,
+ uint8_t peer_addr_type,
+ const uint8_t *peer_addr,
+ const struct ble_gap_adv_params *adv_params,
+ ble_gap_event_fn *cb, void *cb_arg,
+ int fail_idx, uint8_t fail_status);
+int ble_hs_test_util_adv_stop(uint8_t hci_status);
+int ble_hs_test_util_wl_set(struct ble_gap_white_entry *white_list,
+ uint8_t white_list_count,
+ int fail_idx, uint8_t fail_status);
+int ble_hs_test_util_conn_update(uint16_t conn_handle,
+ struct ble_gap_upd_params *params,
+ uint8_t hci_status);
+int ble_hs_test_util_set_our_irk(const uint8_t *irk, int fail_idx,
+ uint8_t hci_status);
+int ble_hs_test_util_security_initiate(uint16_t conn_handle,
+ uint8_t hci_status);
+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 ble_hs_test_util_l2cap_rx(uint16_t conn_handle,
+ struct hci_data_hdr *hci_hdr,
+ struct os_mbuf *om);
+int ble_hs_test_util_l2cap_rx_payload_flat(uint16_t conn_handle, uint16_t cid,
+ const void *data, int len);
+void ble_hs_test_util_rx_hci_buf_size_ack(uint16_t buf_size);
+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);
+void ble_hs_test_util_set_startup_acks(void);
+void ble_hs_test_util_rx_num_completed_pkts_event(
+ struct ble_hs_test_util_num_completed_pkts_entry *entries);
+void ble_hs_test_util_rx_disconn_complete_event(
+ struct hci_disconn_complete *evt);
+uint8_t *ble_hs_test_util_verify_tx_hci(uint8_t ogf, uint16_t ocf,
+ uint8_t *out_param_len);
+void ble_hs_test_util_tx_all(void);
+void ble_hs_test_util_verify_tx_prep_write(uint16_t attr_handle,
+ uint16_t offset,
+ const void *data, int data_len);
+void ble_hs_test_util_verify_tx_exec_write(uint8_t expected_flags);
+void ble_hs_test_util_verify_tx_read_rsp(uint8_t *attr_data, int attr_len);
+void ble_hs_test_util_verify_tx_read_blob_rsp(uint8_t *attr_data,
+ int attr_len);
+void ble_hs_test_util_set_static_rnd_addr(void);
+struct os_mbuf *ble_hs_test_util_om_from_flat(const void *buf, uint16_t len);
+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);
+void ble_hs_test_util_attr_to_flat(struct ble_hs_test_util_flat_attr *flat,
+ const struct ble_gatt_attr *attr);
+void ble_hs_test_util_attr_from_flat(
+ struct ble_gatt_attr *attr, const struct ble_hs_test_util_flat_attr *flat);
+int ble_hs_test_util_read_local_flat(uint16_t attr_handle, uint16_t max_len,
+ void *buf, uint16_t *out_len);
+int ble_hs_test_util_write_local_flat(uint16_t attr_handle,
+ const void *buf, uint16_t buf_len);
+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);
+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);
+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);
+int ble_hs_test_util_mbuf_count(
+ const struct ble_hs_test_util_mbuf_params *params);
+void ble_hs_test_util_assert_mbufs_freed(
+ const struct ble_hs_test_util_mbuf_params *params);
+void ble_hs_test_util_post_test(void *arg);
+void ble_hs_test_util_init_no_start(void);
+void ble_hs_test_util_init(void);
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_hs_test_util_store.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_hs_test_util_store.c b/net/nimble/host/test/src/ble_hs_test_util_store.c
new file mode 100644
index 0000000..f888753
--- /dev/null
+++ b/net/nimble/host/test/src/ble_hs_test_util_store.c
@@ -0,0 +1,248 @@
+/**
+ * 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 "testutil/testutil.h"
+#include "nimble/ble.h"
+#include "ble_hs_test_util.h"
+#include "ble_hs_test_util_store.h"
+
+static int ble_hs_test_util_store_max_our_secs;
+static int ble_hs_test_util_store_max_peer_secs;
+static int ble_hs_test_util_store_max_cccds;
+
+static struct ble_store_value_sec *ble_hs_test_util_store_our_secs;
+static struct ble_store_value_sec *ble_hs_test_util_store_peer_secs;
+static struct ble_store_value_cccd *ble_hs_test_util_store_cccds;
+int ble_hs_test_util_store_num_our_secs;
+int ble_hs_test_util_store_num_peer_secs;
+int ble_hs_test_util_store_num_cccds;
+
+
+#define BLE_HS_TEST_UTIL_STORE_WRITE_GEN(store, num_vals, max_vals, \
+ val, idx) do \
+{ \
+ if ((idx) == -1) { \
+ if ((num_vals) >= (max_vals)) { \
+ return BLE_HS_ENOMEM; \
+ } \
+ store[(num_vals)] = (val); \
+ (num_vals)++; \
+ } else { \
+ store[(idx)] = val; \
+ } \
+ return 0; \
+} while (0)
+
+void
+ble_hs_test_util_store_init(int max_our_secs, int max_peer_secs, int max_cccds)
+{
+ free(ble_hs_test_util_store_our_secs);
+ free(ble_hs_test_util_store_peer_secs);
+ free(ble_hs_test_util_store_cccds);
+
+ ble_hs_test_util_store_our_secs = malloc(
+ ble_hs_test_util_store_max_our_secs *
+ sizeof *ble_hs_test_util_store_our_secs);
+ TEST_ASSERT_FATAL(ble_hs_test_util_store_our_secs != NULL);
+
+ ble_hs_test_util_store_peer_secs = malloc(
+ ble_hs_test_util_store_max_peer_secs *
+ sizeof *ble_hs_test_util_store_peer_secs);
+ TEST_ASSERT_FATAL(ble_hs_test_util_store_peer_secs != NULL);
+
+ ble_hs_test_util_store_cccds = malloc(
+ ble_hs_test_util_store_max_cccds *
+ sizeof *ble_hs_test_util_store_cccds);
+ TEST_ASSERT_FATAL(ble_hs_test_util_store_cccds != NULL);
+
+ ble_hs_test_util_store_max_our_secs = max_our_secs;
+ ble_hs_test_util_store_max_peer_secs = max_peer_secs;
+ ble_hs_test_util_store_max_cccds = max_cccds;
+ ble_hs_test_util_store_num_our_secs = 0;
+ ble_hs_test_util_store_num_peer_secs = 0;
+ ble_hs_test_util_store_num_cccds = 0;
+}
+
+static int
+ble_hs_test_util_store_read_sec(struct ble_store_value_sec *store,
+ int num_values,
+ struct ble_store_key_sec *key,
+ struct ble_store_value_sec *value)
+{
+ struct ble_store_value_sec *cur;
+ int skipped;
+ int i;
+
+ skipped = 0;
+
+ for (i = 0; i < num_values; i++) {
+ cur = store + i;
+
+ if (key->peer_addr_type != BLE_STORE_ADDR_TYPE_NONE) {
+ if (cur->peer_addr_type != key->peer_addr_type) {
+ continue;
+ }
+
+ if (memcmp(cur->peer_addr, key->peer_addr,
+ sizeof cur->peer_addr) != 0) {
+ continue;
+ }
+ }
+
+ if (key->ediv_rand_present) {
+ if (cur->ediv != key->ediv) {
+ continue;
+ }
+
+ if (cur->rand_num != key->rand_num) {
+ continue;
+ }
+ }
+
+ if (key->idx > skipped) {
+ skipped++;
+ continue;
+ }
+
+ *value = *cur;
+ return 0;
+ }
+
+ return BLE_HS_ENOENT;
+}
+
+static int
+ble_hs_test_util_store_find_cccd(struct ble_store_key_cccd *key)
+{
+ struct ble_store_value_cccd *cur;
+ int skipped;
+ int i;
+
+ skipped = 0;
+ for (i = 0; i < ble_hs_test_util_store_num_cccds; i++) {
+ cur = ble_hs_test_util_store_cccds + i;
+
+ if (key->peer_addr_type != BLE_STORE_ADDR_TYPE_NONE) {
+ if (cur->peer_addr_type != key->peer_addr_type) {
+ continue;
+ }
+
+ if (memcmp(cur->peer_addr, key->peer_addr, 6) != 0) {
+ continue;
+ }
+ }
+
+ if (key->chr_val_handle != 0) {
+ if (cur->chr_val_handle != key->chr_val_handle) {
+ continue;
+ }
+ }
+
+ if (key->idx > skipped) {
+ skipped++;
+ continue;
+ }
+
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+ble_hs_test_util_store_read_cccd(struct ble_store_key_cccd *key,
+ struct ble_store_value_cccd *value)
+{
+ int idx;
+
+ idx = ble_hs_test_util_store_find_cccd(key);
+ if (idx == -1) {
+ return BLE_HS_ENOENT;
+ }
+
+ *value = ble_hs_test_util_store_cccds[idx];
+ return 0;
+}
+
+int
+ble_hs_test_util_store_read(int obj_type, union ble_store_key *key,
+ union ble_store_value *dst)
+{
+ switch (obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ return ble_hs_test_util_store_read_sec(
+ ble_hs_test_util_store_peer_secs,
+ ble_hs_test_util_store_num_peer_secs,
+ &key->sec,
+ &dst->sec);
+
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ return ble_hs_test_util_store_read_sec(
+ ble_hs_test_util_store_our_secs,
+ ble_hs_test_util_store_num_our_secs,
+ &key->sec,
+ &dst->sec);
+
+ case BLE_STORE_OBJ_TYPE_CCCD:
+ return ble_hs_test_util_store_read_cccd(&key->cccd, &dst->cccd);
+
+ default:
+ TEST_ASSERT_FATAL(0);
+ return BLE_HS_EUNKNOWN;
+ }
+}
+
+int
+ble_hs_test_util_store_write(int obj_type, union ble_store_value *value)
+{
+ struct ble_store_key_cccd key_cccd;
+ int idx;
+
+ switch (obj_type) {
+ case BLE_STORE_OBJ_TYPE_PEER_SEC:
+ BLE_HS_TEST_UTIL_STORE_WRITE_GEN(
+ ble_hs_test_util_store_peer_secs,
+ ble_hs_test_util_store_num_peer_secs,
+ ble_hs_test_util_store_max_peer_secs,
+ value->sec, -1);
+
+ case BLE_STORE_OBJ_TYPE_OUR_SEC:
+ BLE_HS_TEST_UTIL_STORE_WRITE_GEN(
+ ble_hs_test_util_store_our_secs,
+ ble_hs_test_util_store_num_our_secs,
+ ble_hs_test_util_store_max_our_secs,
+ value->sec, -1);
+
+ case BLE_STORE_OBJ_TYPE_CCCD:
+ ble_store_key_from_value_cccd(&key_cccd, &value->cccd);
+ idx = ble_hs_test_util_store_find_cccd(&key_cccd);
+ BLE_HS_TEST_UTIL_STORE_WRITE_GEN(
+ ble_hs_test_util_store_cccds,
+ ble_hs_test_util_store_num_cccds,
+ ble_hs_test_util_store_max_cccds,
+ value->cccd, idx);
+
+ default:
+ TEST_ASSERT_FATAL(0);
+ return BLE_HS_EUNKNOWN;
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_hs_test_util_store.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_hs_test_util_store.h b/net/nimble/host/test/src/ble_hs_test_util_store.h
new file mode 100644
index 0000000..77ee291
--- /dev/null
+++ b/net/nimble/host/test/src/ble_hs_test_util_store.h
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ */
+
+#ifndef H_BLE_HS_TEST_UTIL_STORE_
+#define H_BLE_HS_TEST_UTIL_STORE_
+
+union ble_store_value;
+union ble_store_key;
+
+extern int ble_hs_test_util_store_num_our_ltks;
+extern int ble_hs_test_util_store_num_peer_ltks;
+extern int ble_hs_test_util_store_num_cccds;
+
+void ble_hs_test_util_store_init(int max_our_ltks, int max_peer_ltks,
+ int max_cccds);
+int ble_hs_test_util_store_read(int obj_type, union ble_store_key *key,
+ union ble_store_value *dst);
+int ble_hs_test_util_store_write(int obj_type, union ble_store_value *value);
+
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/d98ddc1c/net/nimble/host/test/src/ble_l2cap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/test/src/ble_l2cap_test.c b/net/nimble/host/test/src/ble_l2cap_test.c
new file mode 100644
index 0000000..69db2f8
--- /dev/null
+++ b/net/nimble/host/test/src/ble_l2cap_test.c
@@ -0,0 +1,690 @@
+/**
+ * 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 <stddef.h>
+#include <errno.h>
+#include "testutil/testutil.h"
+#include "nimble/hci_common.h"
+#include "host/ble_hs_test.h"
+#include "ble_hs_test_util.h"
+
+#define BLE_L2CAP_TEST_CID 99
+
+static int ble_l2cap_test_update_status;
+static void *ble_l2cap_test_update_arg;
+
+/*****************************************************************************
+ * $util *
+ *****************************************************************************/
+
+#define BLE_L2CAP_TEST_UTIL_HCI_HDR(handle, pb, len) \
+ ((struct hci_data_hdr) { \
+ .hdh_handle_pb_bc = ((handle) << 0) | \
+ ((pb) << 12), \
+ .hdh_len = (len) \
+ })
+
+static void
+ble_l2cap_test_util_init(void)
+{
+ ble_hs_test_util_init();
+ ble_l2cap_test_update_status = -1;
+ ble_l2cap_test_update_arg = (void *)(uintptr_t)-1;
+}
+
+static void
+ble_l2cap_test_util_rx_update_req(uint16_t conn_handle, uint8_t id,
+ struct ble_l2cap_sig_update_params *params)
+{
+ struct ble_l2cap_sig_update_req req;
+ struct hci_data_hdr hci_hdr;
+ struct os_mbuf *om;
+ void *v;
+ int rc;
+
+ hci_hdr = BLE_L2CAP_TEST_UTIL_HCI_HDR(
+ 2, BLE_HCI_PB_FIRST_FLUSH,
+ BLE_L2CAP_HDR_SZ + BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_REQ_SZ);
+
+ rc = ble_l2cap_sig_init_cmd(BLE_L2CAP_SIG_OP_UPDATE_REQ, id,
+ BLE_L2CAP_SIG_UPDATE_REQ_SZ, &om, &v);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ req.itvl_min = params->itvl_min;
+ req.itvl_max = params->itvl_max;
+ req.slave_latency = params->slave_latency;
+ req.timeout_multiplier = params->timeout_multiplier;
+ ble_l2cap_sig_update_req_write(v, BLE_L2CAP_SIG_UPDATE_REQ_SZ, &req);
+
+ ble_hs_test_util_set_ack(
+ ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE,
+ BLE_HCI_OCF_LE_CONN_UPDATE), 0);
+ rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SIG,
+ &hci_hdr, om);
+ TEST_ASSERT_FATAL(rc == 0);
+}
+
+static int
+ble_l2cap_test_util_rx_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;
+ void *v;
+ int rc;
+
+ hci_hdr = BLE_L2CAP_TEST_UTIL_HCI_HDR(
+ 2, BLE_HCI_PB_FIRST_FLUSH,
+ BLE_L2CAP_HDR_SZ + BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ);
+
+ rc = ble_l2cap_sig_init_cmd(BLE_L2CAP_SIG_OP_UPDATE_RSP, id,
+ BLE_L2CAP_SIG_UPDATE_RSP_SZ, &om, &v);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ rsp.result = result;
+ ble_l2cap_sig_update_rsp_write(v, BLE_L2CAP_SIG_UPDATE_RSP_SZ, &rsp);
+
+ rc = ble_hs_test_util_l2cap_rx_first_frag(conn_handle, BLE_L2CAP_CID_SIG,
+ &hci_hdr, om);
+ return rc;
+}
+
+
+static struct os_mbuf *
+ble_l2cap_test_util_verify_tx_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;
+}
+
+/**
+ * @return The L2CAP sig identifier in the request.
+ */
+static uint8_t
+ble_l2cap_test_util_verify_tx_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_l2cap_test_util_verify_tx_sig_hdr(BLE_L2CAP_SIG_OP_UPDATE_REQ, 0,
+ BLE_L2CAP_SIG_UPDATE_REQ_SZ,
+ &hdr);
+
+ /* Verify payload. */
+ ble_l2cap_sig_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_test_util_verify_tx_update_rsp(uint8_t exp_id, uint16_t exp_result)
+{
+ struct ble_l2cap_sig_update_rsp rsp;
+ struct os_mbuf *om;
+
+ om = ble_l2cap_test_util_verify_tx_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);
+}
+
+static void
+ble_l2cap_test_util_verify_tx_update_conn(
+ struct ble_gap_upd_params *params)
+{
+ uint8_t param_len;
+ uint8_t *param;
+
+ param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE,
+ BLE_HCI_OCF_LE_CONN_UPDATE,
+ ¶m_len);
+ TEST_ASSERT(param_len == BLE_HCI_CONN_UPDATE_LEN);
+ TEST_ASSERT(le16toh(param + 0) == 2);
+ TEST_ASSERT(le16toh(param + 2) == params->itvl_min);
+ TEST_ASSERT(le16toh(param + 4) == params->itvl_max);
+ TEST_ASSERT(le16toh(param + 6) == params->latency);
+ TEST_ASSERT(le16toh(param + 8) == params->supervision_timeout);
+ TEST_ASSERT(le16toh(param + 10) == params->min_ce_len);
+ TEST_ASSERT(le16toh(param + 12) == params->max_ce_len);
+}
+
+static int
+ble_l2cap_test_util_dummy_rx(uint16_t conn_handle, struct os_mbuf **om)
+{
+ return 0;
+}
+
+static void
+ble_l2cap_test_util_create_conn(uint16_t conn_handle, uint8_t *addr,
+ ble_gap_event_fn *cb, void *cb_arg)
+{
+ struct ble_l2cap_chan *chan;
+ struct ble_hs_conn *conn;
+
+ ble_hs_test_util_create_conn(conn_handle, addr, cb, cb_arg);
+
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find(conn_handle);
+ TEST_ASSERT_FATAL(conn != NULL);
+
+ chan = ble_l2cap_chan_alloc();
+ TEST_ASSERT_FATAL(chan != NULL);
+
+ chan->blc_cid = BLE_L2CAP_TEST_CID;
+ chan->blc_my_mtu = 240;
+ chan->blc_default_mtu = 240;
+ chan->blc_rx_fn = ble_l2cap_test_util_dummy_rx;
+
+ ble_hs_conn_chan_insert(conn, chan);
+
+ ble_hs_test_util_prev_hci_tx_clear();
+
+ ble_hs_unlock();
+}
+
+static int
+ble_l2cap_test_util_rx_first_frag(uint16_t conn_handle,
+ uint16_t l2cap_frag_len,
+ uint16_t cid, uint16_t l2cap_len)
+{
+ struct hci_data_hdr hci_hdr;
+ struct os_mbuf *om;
+ uint16_t hci_len;
+ void *v;
+ int rc;
+
+ om = ble_hs_mbuf_l2cap_pkt();
+ TEST_ASSERT_FATAL(om != NULL);
+
+ v = os_mbuf_extend(om, l2cap_frag_len);
+ TEST_ASSERT_FATAL(v != NULL);
+
+ om = ble_l2cap_prepend_hdr(om, cid, l2cap_len);
+ TEST_ASSERT_FATAL(om != NULL);
+
+ hci_len = sizeof hci_hdr + l2cap_frag_len;
+ hci_hdr = BLE_L2CAP_TEST_UTIL_HCI_HDR(conn_handle,
+ BLE_HCI_PB_FIRST_FLUSH, hci_len);
+ rc = ble_hs_test_util_l2cap_rx(conn_handle, &hci_hdr, om);
+ return rc;
+}
+
+static int
+ble_l2cap_test_util_rx_next_frag(uint16_t conn_handle, uint16_t hci_len)
+{
+ struct hci_data_hdr hci_hdr;
+ struct os_mbuf *om;
+ void *v;
+ int rc;
+
+ om = ble_hs_mbuf_l2cap_pkt();
+ TEST_ASSERT_FATAL(om != NULL);
+
+ v = os_mbuf_extend(om, hci_len);
+ TEST_ASSERT_FATAL(v != NULL);
+
+ hci_hdr = BLE_L2CAP_TEST_UTIL_HCI_HDR(conn_handle,
+ BLE_HCI_PB_MIDDLE, hci_len);
+ rc = ble_hs_test_util_l2cap_rx(conn_handle, &hci_hdr, om);
+ return rc;
+}
+
+static void
+ble_l2cap_test_util_verify_first_frag(uint16_t conn_handle,
+ uint16_t l2cap_frag_len,
+ uint16_t l2cap_len)
+{
+ struct ble_hs_conn *conn;
+ int rc;
+
+ rc = ble_l2cap_test_util_rx_first_frag(conn_handle, l2cap_frag_len,
+ BLE_L2CAP_TEST_CID, l2cap_len);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find(conn_handle);
+ TEST_ASSERT_FATAL(conn != NULL);
+ TEST_ASSERT(conn->bhc_rx_chan != NULL &&
+ conn->bhc_rx_chan->blc_cid == BLE_L2CAP_TEST_CID);
+
+ ble_hs_unlock();
+}
+
+static void
+ble_l2cap_test_util_verify_middle_frag(uint16_t conn_handle,
+ uint16_t hci_len)
+{
+ struct ble_hs_conn *conn;
+ int rc;
+
+ rc = ble_l2cap_test_util_rx_next_frag(conn_handle, hci_len);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find(conn_handle);
+ TEST_ASSERT_FATAL(conn != NULL);
+ TEST_ASSERT(conn->bhc_rx_chan != NULL &&
+ conn->bhc_rx_chan->blc_cid == BLE_L2CAP_TEST_CID);
+
+ ble_hs_unlock();
+}
+
+static void
+ble_l2cap_test_util_verify_last_frag(uint16_t conn_handle,
+ uint16_t hci_len)
+{
+ struct ble_hs_conn *conn;
+ int rc;
+
+ rc = ble_l2cap_test_util_rx_next_frag(conn_handle, hci_len);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find(conn_handle);
+ TEST_ASSERT_FATAL(conn != NULL);
+ TEST_ASSERT(conn->bhc_rx_chan == NULL);
+
+ ble_hs_unlock();
+}
+
+/*****************************************************************************
+ * $rx *
+ *****************************************************************************/
+
+TEST_CASE(ble_l2cap_test_case_bad_header)
+{
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ NULL, NULL);
+
+ rc = ble_l2cap_test_util_rx_first_frag(2, 14, 1234, 10);
+ TEST_ASSERT(rc == BLE_HS_ENOENT);
+}
+
+/*****************************************************************************
+ * $fragmentation *
+ *****************************************************************************/
+
+TEST_CASE(ble_l2cap_test_case_frag_single)
+{
+ struct hci_data_hdr hci_hdr;
+ struct os_mbuf *om;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ NULL, NULL);
+
+ /*** HCI header specifies middle fragment without start. */
+ hci_hdr = BLE_L2CAP_TEST_UTIL_HCI_HDR(2, BLE_HCI_PB_MIDDLE, 10);
+
+ om = ble_hs_mbuf_l2cap_pkt();
+ TEST_ASSERT_FATAL(om != NULL);
+
+ om = ble_l2cap_prepend_hdr(om, 0, 5);
+ TEST_ASSERT_FATAL(om != NULL);
+
+ rc = ble_hs_test_util_l2cap_rx(2, &hci_hdr, om);
+ TEST_ASSERT(rc == BLE_HS_EBADDATA);
+
+ /*** Packet consisting of three fragments. */
+ ble_l2cap_test_util_verify_first_frag(2, 10, 30);
+ ble_l2cap_test_util_verify_middle_frag(2, 10);
+ ble_l2cap_test_util_verify_last_frag(2, 10);
+
+ /*** Packet consisting of five fragments. */
+ ble_l2cap_test_util_verify_first_frag(2, 8, 49);
+ ble_l2cap_test_util_verify_middle_frag(2, 13);
+ ble_l2cap_test_util_verify_middle_frag(2, 2);
+ ble_l2cap_test_util_verify_middle_frag(2, 21);
+ ble_l2cap_test_util_verify_last_frag(2, 5);
+}
+
+TEST_CASE(ble_l2cap_test_case_frag_multiple)
+{
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ NULL, NULL);
+ ble_l2cap_test_util_create_conn(3, ((uint8_t[]){2,3,4,5,6,7}),
+ NULL, NULL);
+ ble_l2cap_test_util_create_conn(4, ((uint8_t[]){3,4,5,6,7,8}),
+ NULL, NULL);
+
+ ble_l2cap_test_util_verify_first_frag(2, 3, 10);
+ ble_l2cap_test_util_verify_first_frag(3, 2, 5);
+ ble_l2cap_test_util_verify_middle_frag(2, 6);
+ ble_l2cap_test_util_verify_first_frag(4, 1, 4);
+ ble_l2cap_test_util_verify_middle_frag(3, 2);
+ ble_l2cap_test_util_verify_last_frag(3, 1);
+ ble_l2cap_test_util_verify_middle_frag(4, 2);
+ ble_l2cap_test_util_verify_last_frag(4, 1);
+ ble_l2cap_test_util_verify_last_frag(2, 1);
+}
+
+TEST_CASE(ble_l2cap_test_case_frag_channels)
+{
+ struct ble_hs_conn *conn;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ NULL, NULL);
+
+ /* Receive a starting fragment on the first channel. */
+ rc = ble_l2cap_test_util_rx_first_frag(2, 14, BLE_L2CAP_TEST_CID, 30);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_lock();
+ conn = ble_hs_conn_find(2);
+ TEST_ASSERT_FATAL(conn != NULL);
+ TEST_ASSERT(conn->bhc_rx_chan != NULL &&
+ conn->bhc_rx_chan->blc_cid == BLE_L2CAP_TEST_CID);
+ ble_hs_unlock();
+
+ /* Receive a starting fragment on a different channel. The first fragment
+ * should get discarded.
+ */
+ rc = ble_l2cap_test_util_rx_first_frag(2, 14, BLE_L2CAP_CID_ATT, 30);
+ TEST_ASSERT(rc == 0);
+
+ ble_hs_lock();
+ conn = ble_hs_conn_find(2);
+ TEST_ASSERT_FATAL(conn != NULL);
+ TEST_ASSERT(conn->bhc_rx_chan != NULL &&
+ conn->bhc_rx_chan->blc_cid == BLE_L2CAP_CID_ATT);
+ ble_hs_unlock();
+}
+
+/*****************************************************************************
+ * $unsolicited response *
+ *****************************************************************************/
+
+TEST_CASE(ble_l2cap_test_case_sig_unsol_rsp)
+{
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ NULL, NULL);
+
+ /* Receive an unsolicited response. */
+ rc = ble_l2cap_test_util_rx_update_rsp(2, 100, 0);
+ TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+ /* Ensure we did not send anything in return. */
+ ble_hs_test_util_tx_all();
+ TEST_ASSERT_FATAL(ble_hs_test_util_prev_tx_dequeue() == NULL);
+}
+
+/*****************************************************************************
+ * $update *
+ *****************************************************************************/
+
+static int
+ble_l2cap_test_util_conn_cb(struct ble_gap_event *event, void *arg)
+{
+ int *accept;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_L2CAP_UPDATE_REQ:
+ accept = arg;
+ return !*accept;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+ble_l2cap_test_util_peer_updates(int accept)
+{
+ struct ble_l2cap_sig_update_params l2cap_params;
+ struct ble_gap_upd_params params;
+ ble_hs_conn_flags_t conn_flags;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ ble_l2cap_test_util_conn_cb,
+ &accept);
+
+ l2cap_params.itvl_min = 0x200;
+ l2cap_params.itvl_max = 0x300;
+ l2cap_params.slave_latency = 0;
+ l2cap_params.timeout_multiplier = 0x100;
+ ble_l2cap_test_util_rx_update_req(2, 1, &l2cap_params);
+
+ /* Ensure an update response command got sent. */
+ ble_hs_process_tx_data_queue();
+ ble_l2cap_test_util_verify_tx_update_rsp(1, !accept);
+
+ if (accept) {
+ params.itvl_min = 0x200;
+ params.itvl_max = 0x300;
+ params.latency = 0;
+ params.supervision_timeout = 0x100;
+ params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
+ params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
+ ble_l2cap_test_util_verify_tx_update_conn(¶ms);
+ } else {
+ /* Ensure no update got scheduled. */
+ rc = ble_hs_atomic_conn_flags(2, &conn_flags);
+ TEST_ASSERT(rc == 0 && !(conn_flags & BLE_HS_CONN_F_UPDATE));
+ }
+}
+
+static void
+ble_l2cap_test_util_update_cb(int status, void *arg)
+{
+ ble_l2cap_test_update_status = status;
+ ble_l2cap_test_update_arg = arg;
+}
+
+static void
+ble_l2cap_test_util_we_update(int peer_accepts)
+{
+ struct ble_l2cap_sig_update_params params;
+ uint8_t id;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ ble_l2cap_test_util_conn_cb, NULL);
+
+ /* Only the slave can initiate the L2CAP connection update procedure. */
+ ble_hs_atomic_conn_set_flags(2, BLE_HS_CONN_F_MASTER, 0);
+
+ params.itvl_min = 0x200;
+ params.itvl_min = 0x300;
+ params.slave_latency = 0;
+ params.timeout_multiplier = 0x100;
+ rc = ble_l2cap_sig_update(2, ¶ms, ble_l2cap_test_util_update_cb, NULL);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ ble_hs_test_util_tx_all();
+
+ /* Ensure an update request got sent. */
+ id = ble_l2cap_test_util_verify_tx_update_req(¶ms);
+
+ /* Receive response from peer. */
+ rc = ble_l2cap_test_util_rx_update_rsp(2, id, !peer_accepts);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure callback got called. */
+ if (peer_accepts) {
+ TEST_ASSERT(ble_l2cap_test_update_status == 0);
+ } else {
+ TEST_ASSERT(ble_l2cap_test_update_status == BLE_HS_EREJECT);
+ }
+ TEST_ASSERT(ble_l2cap_test_update_arg == NULL);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_accept)
+{
+ ble_l2cap_test_util_peer_updates(1);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_reject)
+{
+ ble_l2cap_test_util_peer_updates(0);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_init_accept)
+{
+ ble_l2cap_test_util_we_update(1);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_init_reject)
+{
+ ble_l2cap_test_util_we_update(0);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_init_fail_master)
+{
+ struct ble_l2cap_sig_update_params params;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ ble_l2cap_test_util_conn_cb, NULL);
+
+ params.itvl_min = 0x200;
+ params.itvl_min = 0x300;
+ params.slave_latency = 0;
+ params.timeout_multiplier = 0x100;
+ rc = ble_l2cap_sig_update(2, ¶ms, ble_l2cap_test_util_update_cb, NULL);
+ TEST_ASSERT_FATAL(rc == BLE_HS_EINVAL);
+
+ /* Ensure callback never called. */
+ ble_hs_test_util_tx_all();
+ TEST_ASSERT(ble_l2cap_test_update_status == -1);
+}
+
+TEST_CASE(ble_l2cap_test_case_sig_update_init_fail_bad_id)
+{
+ struct ble_l2cap_sig_update_params params;
+ uint8_t id;
+ int rc;
+
+ ble_l2cap_test_util_init();
+
+ ble_l2cap_test_util_create_conn(2, ((uint8_t[]){1,2,3,4,5,6}),
+ ble_l2cap_test_util_conn_cb, NULL);
+
+ /* Only the slave can initiate the L2CAP connection update procedure. */
+ ble_hs_atomic_conn_set_flags(2, BLE_HS_CONN_F_MASTER, 0);
+
+ params.itvl_min = 0x200;
+ params.itvl_min = 0x300;
+ params.slave_latency = 0;
+ params.timeout_multiplier = 0x100;
+ rc = ble_l2cap_sig_update(2, ¶ms, ble_l2cap_test_util_update_cb, NULL);
+ TEST_ASSERT_FATAL(rc == 0);
+
+ ble_hs_test_util_tx_all();
+
+ /* Ensure an update request got sent. */
+ id = ble_l2cap_test_util_verify_tx_update_req(¶ms);
+
+ /* Receive response from peer with incorrect ID. */
+ rc = ble_l2cap_test_util_rx_update_rsp(2, id + 1, 0);
+ TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+ /* Ensure callback did not get called. */
+ TEST_ASSERT(ble_l2cap_test_update_status == -1);
+
+ /* Receive response from peer with correct ID. */
+ rc = ble_l2cap_test_util_rx_update_rsp(2, id, 0);
+ TEST_ASSERT(rc == 0);
+
+ /* Ensure callback got called. */
+ TEST_ASSERT(ble_l2cap_test_update_status == 0);
+ TEST_ASSERT(ble_l2cap_test_update_arg == NULL);
+}
+
+TEST_SUITE(ble_l2cap_test_suite)
+{
+ tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
+
+ ble_l2cap_test_case_bad_header();
+ ble_l2cap_test_case_frag_single();
+ ble_l2cap_test_case_frag_multiple();
+ ble_l2cap_test_case_frag_channels();
+ ble_l2cap_test_case_sig_unsol_rsp();
+ ble_l2cap_test_case_sig_update_accept();
+ ble_l2cap_test_case_sig_update_reject();
+ ble_l2cap_test_case_sig_update_init_accept();
+ ble_l2cap_test_case_sig_update_init_reject();
+ ble_l2cap_test_case_sig_update_init_fail_master();
+ ble_l2cap_test_case_sig_update_init_fail_bad_id();
+}
+
+int
+ble_l2cap_test_all(void)
+{
+ ble_l2cap_test_suite();
+
+ return tu_any_failed;
+}