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,
+                                           &param_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,
+                                   &param_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,
+                                           &param_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(&params);
+    } 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, &params, 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(&params);
+
+    /* 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, &params, 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, &params, 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(&params);
+
+    /* 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;
+}