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/05/17 20:51:52 UTC

[29/50] [abbrv] incubator-mynewt-core git commit: BLE Host - Enforce thread safety in unit tests.

BLE Host - Enforce thread safety in unit tests.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/2dd32b7d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/2dd32b7d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/2dd32b7d

Branch: refs/heads/master
Commit: 2dd32b7dfe83359d0f9c7975a9c0c17205687a98
Parents: ee4fbed
Author: Christopher Collins <cc...@apache.org>
Authored: Fri May 13 16:04:27 2016 -0700
Committer: Christopher Collins <cc...@apache.org>
Committed: Fri May 13 17:53:01 2016 -0700

----------------------------------------------------------------------
 net/nimble/host/src/ble_att_clt.c               |  42 +-
 net/nimble/host/src/ble_att_cmd.c               |   2 +-
 net/nimble/host/src/ble_att_priv.h              |   6 +-
 net/nimble/host/src/ble_att_svr.c               |  41 +-
 net/nimble/host/src/ble_gap.c                   |  43 +-
 net/nimble/host/src/ble_gap_priv.h              |   1 -
 net/nimble/host/src/ble_gatt_priv.h             |   5 +-
 net/nimble/host/src/ble_gattc.c                 |  11 +-
 net/nimble/host/src/ble_gatts.c                 | 143 ++++-
 net/nimble/host/src/ble_hs.c                    |  36 +-
 net/nimble/host/src/ble_hs_atomic.c             |  31 +-
 net/nimble/host/src/ble_hs_atomic_priv.h        |   2 +
 net/nimble/host/src/ble_hs_conn.c               |   2 +-
 net/nimble/host/src/ble_hs_conn_priv.h          |   1 +
 net/nimble/host/src/ble_hs_priv.h               |   1 -
 net/nimble/host/src/ble_l2cap_sig.c             |   3 +-
 net/nimble/host/src/test/ble_att_clt_test.c     | 157 +++---
 net/nimble/host/src/test/ble_att_svr_test.c     | 565 ++++++++++---------
 net/nimble/host/src/test/ble_gap_test.c         |  60 +-
 net/nimble/host/src/test/ble_gatt_disc_c_test.c |  34 +-
 net/nimble/host/src/test/ble_gatt_disc_d_test.c |  22 +-
 net/nimble/host/src/test/ble_gatt_disc_s_test.c |  46 +-
 net/nimble/host/src/test/ble_gatt_find_s_test.c |  55 +-
 net/nimble/host/src/test/ble_gatt_read_test.c   | 115 ++--
 net/nimble/host/src/test/ble_gatt_write_test.c  |  90 ++-
 .../host/src/test/ble_gatts_notify_test.c       |  67 +--
 net/nimble/host/src/test/ble_hs_conn_test.c     |  40 +-
 net/nimble/host/src/test/ble_hs_test_util.c     |  48 +-
 net/nimble/host/src/test/ble_hs_test_util.h     |  16 +-
 net/nimble/host/src/test/ble_l2cap_sm_test.c    | 155 +++--
 net/nimble/host/src/test/ble_l2cap_test.c       | 206 ++++---
 31 files changed, 1148 insertions(+), 898 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_att_clt.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_clt.c b/net/nimble/host/src/ble_att_clt.c
index 1a95bdc..5f628c6 100644
--- a/net/nimble/host/src/ble_att_clt.c
+++ b/net/nimble/host/src/ble_att_clt.c
@@ -59,9 +59,6 @@ static int
 ble_att_clt_append_blob(uint16_t conn_handle, struct os_mbuf *txom,
                         void *blob, int blob_len)
 {
-    uint16_t mtu;
-    int extra_len;
-    int cmd_len;
     int rc;
 
     if (blob_len < 0) {
@@ -71,13 +68,6 @@ ble_att_clt_append_blob(uint16_t conn_handle, struct os_mbuf *txom,
         return 0;
     }
 
-    mtu = ble_att_mtu(conn_handle);
-    cmd_len = OS_MBUF_PKTLEN(txom) + blob_len;
-    extra_len = cmd_len - mtu;
-    if (extra_len > 0) {
-        blob_len -= extra_len;
-    }
-
     rc = os_mbuf_append(txom, blob, blob_len);
     if (rc != 0) {
         return rc;
@@ -110,10 +100,14 @@ ble_att_clt_copy_attr_to_flatbuf(struct os_mbuf *om, void **out_attr_val,
 }
 
 static int
-ble_att_clt_tx_req(uint16_t conn_handle, struct os_mbuf *txom)
+ble_att_clt_tx_req_flags(uint16_t conn_handle, struct os_mbuf *txom,
+                         ble_hs_conn_flags_t flags_on_success)
 {
     struct ble_l2cap_chan *chan;
     struct ble_hs_conn *conn;
+    uint16_t total_len;
+    uint16_t mtu;
+    int extra_len;
     int rc;
 
     BLE_HS_DBG_ASSERT_EVAL(txom->om_len >= 1);
@@ -123,8 +117,22 @@ ble_att_clt_tx_req(uint16_t conn_handle, struct os_mbuf *txom)
 
     rc = ble_att_conn_chan_find(conn_handle, &conn, &chan);
     if (rc == 0) {
+        /* Reduce the size of the transmission to fit the connection's ATT
+         * MTU.
+         */
+        total_len = OS_MBUF_PKTLEN(txom);
+        mtu = ble_l2cap_chan_mtu(chan);
+        extra_len = total_len - mtu;
+        if (extra_len > 0) {
+            os_mbuf_adj(txom, -extra_len);
+        }
+
         rc = ble_l2cap_tx(conn, chan, txom);
         txom = NULL;
+
+        if (rc == 0) {
+            conn->bhc_flags |= flags_on_success;
+        }
     }
 
     ble_hs_unlock();
@@ -133,6 +141,15 @@ ble_att_clt_tx_req(uint16_t conn_handle, struct os_mbuf *txom)
     return rc;
 }
 
+static int
+ble_att_clt_tx_req(uint16_t conn_handle, struct os_mbuf *txom)
+{
+    int rc;
+
+    rc = ble_att_clt_tx_req_flags(conn_handle, txom, 0);
+    return rc;
+}
+
 /*****************************************************************************
  * $error response                                                           *
  *****************************************************************************/
@@ -1437,7 +1454,8 @@ ble_att_clt_tx_indicate(uint16_t conn_handle,
         return rc;
     }
 
-    rc = ble_att_clt_tx_req(conn_handle, txom);
+    rc = ble_att_clt_tx_req_flags(conn_handle, txom,
+                                  BLE_HS_CONN_F_INDICATE_TXED);
     if (rc != 0) {
         return rc;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_att_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_cmd.c b/net/nimble/host/src/ble_att_cmd.c
index 9fa6b21..2384ce1 100644
--- a/net/nimble/host/src/ble_att_cmd.c
+++ b/net/nimble/host/src/ble_att_cmd.c
@@ -614,7 +614,7 @@ ble_att_indicate_req_parse(void *payload, int len,
 
 void
 ble_att_indicate_req_write(void *payload, int len,
-                         struct ble_att_indicate_req *src)
+                           struct ble_att_indicate_req *src)
 {
     struct ble_att_indicate_req *dst;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_att_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_priv.h b/net/nimble/host/src/ble_att_priv.h
index 77b5935..3cc19e9 100644
--- a/net/nimble/host/src/ble_att_priv.h
+++ b/net/nimble/host/src/ble_att_priv.h
@@ -115,9 +115,11 @@ struct ble_att_prep_entry {
     struct os_mbuf *bape_value;
 };
 
+SLIST_HEAD(ble_att_prep_entry_list, ble_att_prep_entry);
+
 struct ble_att_svr_conn {
     /** This list is sorted by attribute handle ID. */
-    SLIST_HEAD(, ble_att_prep_entry) basc_prep_list;
+    struct ble_att_prep_entry_list basc_prep_list;
     uint32_t basc_prep_write_rx_time;
 };
 
@@ -184,7 +186,7 @@ int ble_att_svr_rx_notify(uint16_t conn_handle,
                           struct os_mbuf **rxom);
 int ble_att_svr_rx_indicate(uint16_t conn_handle,
                             struct os_mbuf **rxom);
-void ble_att_svr_prep_clear(struct ble_att_svr_conn *basc);
+void ble_att_svr_prep_clear(struct ble_att_prep_entry_list *prep_list);
 int ble_att_svr_read_handle(uint16_t conn_handle, uint16_t attr_handle,
                             struct ble_att_svr_access_ctxt *ctxt,
                             uint8_t *out_att_err);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_att_svr.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_svr.c b/net/nimble/host/src/ble_att_svr.c
index 76bf0e8..7690a27 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -2212,12 +2212,12 @@ ble_att_svr_prep_find_prev(struct ble_att_svr_conn *basc, uint16_t handle,
 }
 
 void
-ble_att_svr_prep_clear(struct ble_att_svr_conn *basc)
+ble_att_svr_prep_clear(struct ble_att_prep_entry_list *prep_list)
 {
     struct ble_att_prep_entry *entry;
 
-    while ((entry = SLIST_FIRST(&basc->basc_prep_list)) != NULL) {
-        SLIST_REMOVE_HEAD(&basc->basc_prep_list, bape_next);
+    while ((entry = SLIST_FIRST(prep_list)) != NULL) {
+        SLIST_REMOVE_HEAD(prep_list, bape_next);
         ble_att_svr_prep_free(entry);
     }
 }
@@ -2226,14 +2226,15 @@ ble_att_svr_prep_clear(struct ble_att_svr_conn *basc)
  * @return                      0 on success; ATT error code on failure.
  */
 static int
-ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
+ble_att_svr_prep_validate(struct ble_att_prep_entry_list *prep_list,
+                          uint16_t *err_handle)
 {
     struct ble_att_prep_entry *entry;
     struct ble_att_prep_entry *prev;
     int cur_len;
 
     prev = NULL;
-    SLIST_FOREACH(entry, &basc->basc_prep_list, bape_next) {
+    SLIST_FOREACH(entry, prep_list, bape_next) {
         if (prev == NULL || prev->bape_handle != entry->bape_handle) {
             /* Ensure attribute write starts at offset 0. */
             if (entry->bape_offset != 0) {
@@ -2266,7 +2267,9 @@ ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
  * @return                      0 on success; ATT error code on failure.
  */
 static int
-ble_att_svr_prep_write(struct ble_hs_conn *conn, uint16_t *err_handle)
+ble_att_svr_prep_write(uint16_t conn_handle,
+                       struct ble_att_prep_entry_list *prep_list,
+                       uint16_t *err_handle)
 {
     struct ble_att_svr_access_ctxt ctxt;
     struct ble_att_prep_entry *entry;
@@ -2280,7 +2283,7 @@ ble_att_svr_prep_write(struct ble_hs_conn *conn, uint16_t *err_handle)
     *err_handle = 0; /* Silence unnecessary warning. */
 
     /* First, validate the contents of the prepare queue. */
-    rc = ble_att_svr_prep_validate(&conn->bhc_att_svr, err_handle);
+    rc = ble_att_svr_prep_validate(prep_list, err_handle);
     if (rc != 0) {
         return rc;
     }
@@ -2289,7 +2292,7 @@ ble_att_svr_prep_write(struct ble_hs_conn *conn, uint16_t *err_handle)
 
     /* Contents are valid; perform the writes. */
     buf_off = 0;
-    entry = SLIST_FIRST(&conn->bhc_att_svr.basc_prep_list);
+    entry = SLIST_FIRST(prep_list);
     while (entry != NULL) {
         next = SLIST_NEXT(entry, bape_next);
 
@@ -2309,7 +2312,7 @@ ble_att_svr_prep_write(struct ble_hs_conn *conn, uint16_t *err_handle)
 
             ctxt.attr_data = flat_buf;
             ctxt.data_len = buf_off;
-            rc = ble_att_svr_write(conn->bhc_handle, attr, &ctxt, &att_err);
+            rc = ble_att_svr_write(conn_handle, attr, &ctxt, &att_err);
             if (rc != 0) {
                 *err_handle = entry->bape_handle;
                 return att_err;
@@ -2506,6 +2509,7 @@ ble_att_svr_rx_exec_write(uint16_t conn_handle, struct os_mbuf **rxom)
     return BLE_HS_ENOTSUP;
 #endif
 
+    struct ble_att_prep_entry_list prep_list;
     struct ble_att_exec_write_req req;
     struct ble_hs_conn *conn;
     struct os_mbuf *txom;
@@ -2540,18 +2544,29 @@ done:
         if (conn == NULL) {
             rc = BLE_HS_ENOTCONN;
         } else {
+            /* Extract the list of prepared writes from the connection so
+             * that they can be processed after the mutex is unlocked.  They
+             * aren't processed now because attribute writes involve executing
+             * an application callback.
+             */
+            prep_list = conn->bhc_att_svr.basc_prep_list;
+            SLIST_INIT(&conn->bhc_att_svr.basc_prep_list);
+        }
+        ble_hs_unlock();
+
+        if (conn != NULL) {
             if (req.baeq_flags & BLE_ATT_EXEC_WRITE_F_CONFIRM) {
                 /* Perform attribute writes. */
-                att_err = ble_att_svr_prep_write(conn, &err_handle);
+                att_err = ble_att_svr_prep_write(conn_handle, &prep_list,
+                                                 &err_handle);
                 if (att_err != 0) {
                     rc = BLE_HS_EAPP;
                 }
             }
 
-            /* Erase all prep entries. */
-            ble_att_svr_prep_clear(&conn->bhc_att_svr);
+            /* Free the prep entries. */
+            ble_att_svr_prep_clear(&prep_list);
         }
-        ble_hs_unlock();
     }
 
     rc = ble_att_svr_tx_rsp(conn_handle, rc, txom, BLE_ATT_OP_EXEC_WRITE_REQ,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_gap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index a0ca36c..14c921a 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -432,33 +432,6 @@ ble_gap_call_master_disc_cb(int event, int status, struct ble_hs_adv *adv,
     }
 }
 
-int
-ble_gap_update_in_progress(uint16_t conn_handle)
-{
-    struct ble_hs_conn *conn;
-
-    conn = ble_hs_conn_find(conn_handle);
-    return conn != NULL && conn->bhc_flags & BLE_HS_CONN_F_UPDATE;
-}
-
-static int
-ble_gap_update_set_flag(uint16_t conn_handle, int val)
-{
-    struct ble_hs_conn *conn;
-
-    conn = ble_hs_conn_find(conn_handle);
-    if (conn == NULL) {
-        return BLE_HS_ENOTCONN;
-    } else {
-        if (val) {
-            conn->bhc_flags |= BLE_HS_CONN_F_UPDATE;
-        } else {
-            conn->bhc_flags &= ~BLE_HS_CONN_F_UPDATE;
-        }
-        return 0;
-    }
-}
-
 static void
 ble_gap_update_notify(uint16_t conn_handle, int status)
 {
@@ -514,7 +487,7 @@ static void
 ble_gap_update_failed(uint16_t conn_handle, int status)
 {
     STATS_INC(ble_gap_stats, update_fail);
-    ble_gap_update_set_flag(conn_handle, 0);
+    ble_hs_atomic_conn_set_flags(conn_handle, BLE_HS_CONN_F_UPDATE, 0);
     ble_gap_update_notify(conn_handle, status);
 }
 
@@ -600,7 +573,8 @@ ble_gap_rx_update_complete(struct hci_le_conn_upd_complete *evt)
         ble_gap_conn_to_snapshot(conn, &snap);
     }
 
-    ble_gap_update_set_flag(evt->connection_handle, 0);
+    conn->bhc_flags &= ~BLE_HS_CONN_F_UPDATE;
+
     ble_hs_unlock();
 
     if (conn != NULL) {
@@ -1913,7 +1887,8 @@ ble_gap_rx_param_req(struct hci_le_conn_param_req *evt)
         if (rc != 0) {
             ble_gap_update_failed(evt->connection_handle, rc);
         } else {
-            ble_gap_update_set_flag(evt->connection_handle, 1);
+            ble_hs_atomic_conn_set_flags(evt->connection_handle,
+                                         BLE_HS_CONN_F_UPDATE, 1);
         }
     } else {
         ble_gap_tx_param_neg_reply(evt->connection_handle, reject_reason);
@@ -1955,18 +1930,20 @@ ble_gap_update_params(uint16_t conn_handle, struct ble_gap_upd_params *params)
     return BLE_HS_ENOTSUP;
 #endif
 
+    struct ble_hs_conn *conn;
     int rc;
 
     ble_hs_lock();
 
     STATS_INC(ble_gap_stats, update);
 
-    if (!ble_hs_conn_exists(conn_handle)) {
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn == NULL) {
         rc = BLE_HS_ENOTCONN;
         goto done;
     }
 
-    if (ble_gap_update_in_progress(conn_handle)) {
+    if (conn->bhc_flags & BLE_HS_CONN_F_UPDATE) {
         rc = BLE_HS_EALREADY;
         goto done;
     }
@@ -1980,7 +1957,7 @@ ble_gap_update_params(uint16_t conn_handle, struct ble_gap_upd_params *params)
         goto done;
     }
 
-    ble_gap_update_set_flag(conn_handle, 1);
+    conn->bhc_flags |= BLE_HS_CONN_F_UPDATE;
 
 done:
     if (rc != 0) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index e84a276..d54328d 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -84,7 +84,6 @@ int ble_gap_ltk_event(uint16_t conn_handle,
                       struct ble_gap_ltk_params *ltk_params);
 int ble_gap_master_in_progress(void);
 int ble_gap_slave_in_progress(void);
-int ble_gap_update_in_progress(uint16_t conn_handle);
 
 void ble_gap_heartbeat(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_gatt_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt_priv.h b/net/nimble/host/src/ble_gatt_priv.h
index 44f79ec..9a4c4fb 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -91,11 +91,8 @@ typedef uint8_t ble_gatts_conn_flags;
 struct ble_gatts_conn {
     struct ble_gatts_clt_cfg *clt_cfgs;
     int num_clt_cfgs;
-    ble_gatts_conn_flags flags;
 };
 
-#define BLE_GATTS_CONN_F_INDICATION_TXED        0x01
-
 /*** @client. */
 int ble_gattc_locked_by_cur_task(void);
 int ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
@@ -145,7 +142,7 @@ int ble_gattc_init(void);
 #define BLE_GATTS_INC_SVC_LEN_NO_UUID           4
 #define BLE_GATTS_INC_SVC_LEN_UUID              6
 
-void ble_gatts_send_notifications(struct ble_hs_conn *conn);
+void ble_gatts_send_updates_for_conn(uint16_t conn_handle);
 
 /*** @misc. */
 void ble_gatts_conn_deinit(struct ble_gatts_conn *gatts_conn);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_gattc.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gattc.c b/net/nimble/host/src/ble_gattc.c
index 4e23f90..7bfd2dd 100644
--- a/net/nimble/host/src/ble_gattc.c
+++ b/net/nimble/host/src/ble_gattc.c
@@ -3658,12 +3658,14 @@ ble_gattc_indicate_rx_rsp(struct ble_gattc_proc *proc)
     ble_hs_lock();
     conn = ble_hs_conn_find(proc->conn_handle);
     if (conn != NULL) {
-        conn->bhc_gatt_svr.flags &= ~BLE_GATTS_CONN_F_INDICATION_TXED;
-
-        /* Send the next indication if one is pending. */
-        ble_gatts_send_notifications(conn);
+        conn->bhc_flags &= ~BLE_HS_CONN_F_INDICATE_TXED;
     }
     ble_hs_unlock();
+
+    /* Send the next indication if one is pending. */
+    if (conn != NULL) {
+        ble_gatts_send_updates_for_conn(proc->conn_handle);
+    }
 }
 
 /**
@@ -3702,6 +3704,7 @@ ble_gattc_indicate(uint16_t conn_handle, uint16_t chr_val_handle,
                                  &ctxt, NULL);
     if (rc != 0) {
         /* Fatal error; application disallowed attribute read. */
+        BLE_HS_DBG_ASSERT(0);
         rc = BLE_HS_EAPP;
         goto done;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_gatts.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatts.c b/net/nimble/host/src/ble_gatts.c
index b0d5c7c..f6fad75 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -980,39 +980,83 @@ ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn)
     return 0;
 }
 
-void
-ble_gatts_send_notifications(struct ble_hs_conn *conn)
+static int
+ble_gatts_send_one_update(uint16_t conn_handle)
 {
     struct ble_gatts_clt_cfg *clt_cfg;
+    struct ble_hs_conn *conn;
+    uint16_t chr_val_handle;
+    uint8_t att_op;
     int rc;
     int i;
 
-    /* Iterate through each configurable characteristic.  If a characteristic
-     * has been updated, try to send an indication or notification
-     * (never both).
-     */
-    for (i = 0; i < conn->bhc_gatt_svr.num_clt_cfgs; i++) {
-        clt_cfg = conn->bhc_gatt_svr.clt_cfgs + i;
+    /* Silence spurious gcc warning. */
+    chr_val_handle = 0;
 
-        if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_UPDATED) {
-            if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_INDICATE) {
-                if (!(conn->bhc_gatt_svr.flags &
-                      BLE_GATTS_CONN_F_INDICATION_TXED)) {
+    /* Assume no pending updates. */
+    att_op = 0;
 
-                    rc = ble_gattc_indicate(conn->bhc_handle,
-                                            clt_cfg->chr_def_handle + 1,
-                                            NULL, NULL);
-                    if (rc == 0) {
-                        conn->bhc_gatt_svr.flags |=
-                            BLE_GATTS_CONN_F_INDICATION_TXED;
-                        clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_UPDATED;
-                    }
+    ble_hs_lock();
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn != NULL) {
+        for (i = 0; i < conn->bhc_gatt_svr.num_clt_cfgs; i++) {
+            clt_cfg = conn->bhc_gatt_svr.clt_cfgs + i;
+            if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_UPDATED) {
+                if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_NOTIFY) {
+                    att_op = BLE_ATT_OP_NOTIFY_REQ;
+                } else if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_INDICATE &&
+                           !(conn->bhc_flags & BLE_HS_CONN_F_INDICATE_TXED)) {
+                    att_op = BLE_ATT_OP_INDICATE_REQ;
                 }
-            } else if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_NOTIFY) {
-                rc = ble_gattc_notify(conn->bhc_handle,
-                                      clt_cfg->chr_def_handle + 1);
-                if (rc == 0) {
+
+                if (att_op != 0) {
+                    chr_val_handle = clt_cfg->chr_def_handle + 1;
                     clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_UPDATED;
+                    break;
+                }
+            }
+        }
+    }
+    ble_hs_unlock();
+
+    switch (att_op) {
+    case 0:
+        return BLE_HS_EDONE;
+
+    case BLE_ATT_OP_NOTIFY_REQ:
+        rc = ble_gattc_notify(conn_handle, chr_val_handle);
+        return rc;
+        
+    case BLE_ATT_OP_INDICATE_REQ:
+        rc = ble_gattc_indicate(conn_handle, chr_val_handle, NULL, NULL);
+        return rc;
+
+    default:
+        BLE_HS_DBG_ASSERT(0);
+        return BLE_HS_EUNKNOWN;
+    }
+}
+
+static void
+ble_gatts_send_updates(uint16_t *conn_handles, int num_conns)
+{
+    int more_sends;
+    int rc;
+    int i;
+
+    more_sends = 1;
+
+    while (more_sends) {
+        for (i = 0; i < num_conns; i++) {
+            more_sends = 0;
+            for (i = 0; i < num_conns; i++) {
+                if (conn_handles[i] != BLE_HS_CONN_HANDLE_NONE) {
+                    rc = ble_gatts_send_one_update(conn_handles[i]);
+                    if (rc == 0) {
+                        more_sends = 1;
+                    } else {
+                        conn_handles[i] = BLE_HS_CONN_HANDLE_NONE;
+                    }
                 }
             }
         }
@@ -1020,10 +1064,47 @@ ble_gatts_send_notifications(struct ble_hs_conn *conn)
 }
 
 void
+ble_gatts_send_updates_for_conn(uint16_t conn_handle)
+{
+    ble_gatts_send_updates(&conn_handle, 1);
+}
+
+static int
+ble_gatts_conns_with_pending_updates(uint16_t *conn_handles)
+{
+    struct ble_gatts_clt_cfg *clt_cfg;
+    struct ble_hs_conn *conn;
+    int num_conns;
+    int i;
+
+    num_conns = 0;
+
+    for (conn = ble_hs_conn_first();
+         conn != NULL;
+         conn = SLIST_NEXT(conn, bhc_next)) {
+
+        /* XXX: Consider caching this information as a connection flag. */
+        for (i = 0; i < conn->bhc_gatt_svr.num_clt_cfgs; i++) {
+            clt_cfg = conn->bhc_gatt_svr.clt_cfgs + i;
+
+            if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_UPDATED) {
+                conn_handles[num_conns++] = conn->bhc_handle;
+                break;
+            }
+        }
+    }
+
+    return num_conns;
+}
+
+void
 ble_gatts_chr_updated(uint16_t chr_def_handle)
 {
     struct ble_gatts_clt_cfg *clt_cfg;
     struct ble_hs_conn *conn;
+    uint16_t conn_handles[NIMBLE_OPT_MAX_CONNECTIONS];
+    int any_updates;
+    int num_conns;
     int idx;
 
     /* Determine if notifications / indications are enabled for this
@@ -1034,6 +1115,9 @@ ble_gatts_chr_updated(uint16_t chr_def_handle)
         return;
     }
 
+    /* Assume no peers are subscribed to this characteristic. */
+    any_updates = 0;
+
     ble_hs_lock();
 
     for (conn = ble_hs_conn_first();
@@ -1048,12 +1132,19 @@ ble_gatts_chr_updated(uint16_t chr_def_handle)
             (BLE_GATTS_CLT_CFG_F_NOTIFY | BLE_GATTS_CLT_CFG_F_INDICATE)) {
 
             clt_cfg->flags |= BLE_GATTS_CLT_CFG_F_UPDATED;
-
-            ble_gatts_send_notifications(conn);
+            any_updates = 1;
         }
     }
 
+    if (any_updates) {
+        num_conns = ble_gatts_conns_with_pending_updates(conn_handles);
+    }
+
     ble_hs_unlock();
+
+    if (any_updates) {
+        ble_gatts_send_updates(conn_handles, num_conns);
+    }
 }
 
 static void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index 3bf9f35..bc83153 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -76,6 +76,10 @@ static struct os_mqueue ble_hs_tx_q;
 
 static struct os_mutex ble_hs_mutex;
 
+#if BLE_HS_DEBUG
+static uint8_t ble_hs_dbg_mutex_locked;
+#endif
+
 STATS_SECT_DECL(ble_hs_stats) ble_hs_stats;
 STATS_NAME_START(ble_hs_stats)
     STATS_NAME(ble_hs_stats, conn_create)
@@ -87,16 +91,16 @@ STATS_NAME_START(ble_hs_stats)
 STATS_NAME_END(ble_hs_stats)
 
 int
-ble_hs_locked(void)
-{
-    return ble_hs_mutex.mu_level > 0;
-}
-
-int
 ble_hs_locked_by_cur_task(void)
 {
     struct os_task *owner;
 
+#if BLE_HS_DEBUG
+    if (!os_started()) {
+        return ble_hs_dbg_mutex_locked;
+    }
+#endif
+
     owner = ble_hs_mutex.mu_owner;
     return owner != NULL && owner == os_sched_get_current_task();
 }
@@ -104,7 +108,7 @@ ble_hs_locked_by_cur_task(void)
 int
 ble_hs_thread_safe(void)
 {
-    return !os_started() || ble_hs_locked_by_cur_task();
+    return ble_hs_locked_by_cur_task();
 }
 
 /**
@@ -123,6 +127,13 @@ ble_hs_lock(void)
 
     BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
 
+#if BLE_HS_DEBUG
+    if (!os_started()) {
+        ble_hs_dbg_mutex_locked = 1;
+        return;
+    }
+#endif
+
     rc = os_mutex_pend(&ble_hs_mutex, 0xffffffff);
     BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
 }
@@ -132,6 +143,14 @@ ble_hs_unlock(void)
 {
     int rc;
 
+#if BLE_HS_DEBUG
+    if (!os_started()) {
+        BLE_HS_DBG_ASSERT(ble_hs_dbg_mutex_locked);
+        ble_hs_dbg_mutex_locked = 0;
+        return;
+    }
+#endif
+
     rc = os_mutex_release(&ble_hs_mutex);
     BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
 }
@@ -411,6 +430,9 @@ ble_hs_init(struct os_eventq *app_evq, struct ble_hs_cfg *cfg)
         rc = BLE_HS_EOS;
         goto err;
     }
+#if BLE_HS_DEBUG
+    ble_hs_dbg_mutex_locked = 0;
+#endif
 
     return 0;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs_atomic.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_atomic.c b/net/nimble/host/src/ble_hs_atomic.c
index a09eaad..36a603d 100644
--- a/net/nimble/host/src/ble_hs_atomic.c
+++ b/net/nimble/host/src/ble_hs_atomic.c
@@ -57,7 +57,36 @@ ble_hs_atomic_conn_flags(uint16_t conn_handle, ble_hs_conn_flags_t *out_flags)
         rc = BLE_HS_ENOTCONN;
     } else {
         rc = 0;
-        *out_flags = conn->bhc_flags;
+        if (out_flags != NULL) {
+            *out_flags = conn->bhc_flags;
+        }
+    }
+
+    ble_hs_unlock();
+
+    return rc;
+}
+
+int
+ble_hs_atomic_conn_set_flags(uint16_t conn_handle, ble_hs_conn_flags_t flags,
+                             int on)
+{
+    struct ble_hs_conn *conn;
+    int rc;
+
+    ble_hs_lock();
+
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+    } else {
+        rc = 0;
+
+        if (on) {
+            conn->bhc_flags |= flags;
+        } else {
+            conn->bhc_flags &= ~flags;
+        }
     }
 
     ble_hs_unlock();

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs_atomic_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_atomic_priv.h b/net/nimble/host/src/ble_hs_atomic_priv.h
index 63a9a36..a08b7ca 100644
--- a/net/nimble/host/src/ble_hs_atomic_priv.h
+++ b/net/nimble/host/src/ble_hs_atomic_priv.h
@@ -26,5 +26,7 @@ int ble_hs_atomic_conn_delete(uint16_t conn_handle);
 void ble_hs_atomic_conn_insert(struct ble_hs_conn *conn);
 int ble_hs_atomic_conn_flags(uint16_t conn_handle,
                              ble_hs_conn_flags_t *out_flags);
+int ble_hs_atomic_conn_set_flags(uint16_t conn_handle,
+                                 ble_hs_conn_flags_t flags, int on);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.c b/net/nimble/host/src/ble_hs_conn.c
index 4071f45..567da51 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -187,7 +187,7 @@ ble_hs_conn_free(struct ble_hs_conn *conn)
 
     ble_gatts_conn_deinit(&conn->bhc_gatt_svr);
 
-    ble_att_svr_prep_clear(&conn->bhc_att_svr);
+    ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
 
     while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
         ble_hs_conn_delete_chan(conn, chan);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs_conn_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn_priv.h b/net/nimble/host/src/ble_hs_conn_priv.h
index aba0e94..12e3fb0 100644
--- a/net/nimble/host/src/ble_hs_conn_priv.h
+++ b/net/nimble/host/src/ble_hs_conn_priv.h
@@ -31,6 +31,7 @@ typedef uint8_t ble_hs_conn_flags_t;
 
 #define BLE_HS_CONN_F_MASTER        0x01
 #define BLE_HS_CONN_F_UPDATE        0x02
+#define BLE_HS_CONN_F_INDICATE_TXED 0x04
 
 struct ble_hs_conn {
     SLIST_ENTRY(ble_hs_conn) bhc_next;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_hs_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_priv.h b/net/nimble/host/src/ble_hs_priv.h
index 024b964..a436028 100644
--- a/net/nimble/host/src/ble_hs_priv.h
+++ b/net/nimble/host/src/ble_hs_priv.h
@@ -95,7 +95,6 @@ int ble_hs_misc_conn_chan_find_reqd(uint16_t conn_handle, uint16_t cid,
 
 void ble_hs_cfg_init(struct ble_hs_cfg *cfg);
 
-int ble_hs_locked(void);
 int ble_hs_locked_by_cur_task(void);
 int ble_hs_thread_safe(void);
 int ble_hs_is_parent_task(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/ble_l2cap_sig.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sig.c b/net/nimble/host/src/ble_l2cap_sig.c
index 13c830b..ddb69e7 100644
--- a/net/nimble/host/src/ble_l2cap_sig.c
+++ b/net/nimble/host/src/ble_l2cap_sig.c
@@ -166,9 +166,8 @@ ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc *proc)
 static void
 ble_l2cap_sig_proc_insert(struct ble_l2cap_sig_proc *proc)
 {
-    ble_hs_lock();
+    BLE_HS_DBG_ASSERT(ble_hs_thread_safe());
     STAILQ_INSERT_HEAD(&ble_l2cap_sig_procs, proc, next);
-    ble_hs_unlock();
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/2dd32b7d/net/nimble/host/src/test/ble_att_clt_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_clt_test.c b/net/nimble/host/src/test/ble_att_clt_test.c
index cd19431..dcb402e 100644
--- a/net/nimble/host/src/test/ble_att_clt_test.c
+++ b/net/nimble/host/src/test/ble_att_clt_test.c
@@ -24,16 +24,16 @@
 #include "host/ble_hs_test.h"
 #include "ble_hs_test_util.h"
 
-static void
-ble_att_clt_test_misc_init(struct ble_hs_conn **conn,
-                           struct ble_l2cap_chan **att_chan)
+/**
+ * @return                      The handle of the new test connection.
+ */
+static uint16_t
+ble_att_clt_test_misc_init(void)
 {
     ble_hs_test_util_init();
-
-    *conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}),
-                                         NULL, NULL);
-    *att_chan = ble_hs_conn_chan_find(*conn, BLE_L2CAP_CID_ATT);
-    TEST_ASSERT_FATAL(*att_chan != NULL);
+    ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}), NULL,
+                                 NULL);
+    return 2;
 }
 
 static void
@@ -76,48 +76,46 @@ ble_att_clt_test_tx_write_req_or_cmd(uint16_t conn_handle,
 TEST_CASE(ble_att_clt_test_tx_find_info)
 {
     struct ble_att_find_info_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Success. */
     req.bafq_start_handle = 1;
     req.bafq_end_handle = 0xffff;
-    rc = ble_att_clt_tx_find_info(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_find_info(conn_handle, &req);
     TEST_ASSERT(rc == 0);
 
     /*** Error: start handle of 0. */
     req.bafq_start_handle = 0;
     req.bafq_end_handle = 0xffff;
-    rc = ble_att_clt_tx_find_info(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_find_info(conn_handle, &req);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 
     /*** Error: start handle greater than end handle. */
     req.bafq_start_handle = 500;
     req.bafq_end_handle = 499;
-    rc = ble_att_clt_tx_find_info(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_find_info(conn_handle, &req);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 
     /*** Success; start and end handles equal. */
     req.bafq_start_handle = 500;
     req.bafq_end_handle = 500;
-    rc = ble_att_clt_tx_find_info(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_find_info(conn_handle, &req);
     TEST_ASSERT(rc == 0);
 }
 
 TEST_CASE(ble_att_clt_test_rx_find_info)
 {
     struct ble_att_find_info_rsp rsp;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t buf[1024];
     uint8_t uuid128_1[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
     int off;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** One 128-bit UUID. */
     /* Receive response with attribute mapping. */
@@ -131,7 +129,8 @@ TEST_CASE(ble_att_clt_test_rx_find_info)
     memcpy(buf + off, uuid128_1, 16);
     off += 16;
 
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, off);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, off);
     TEST_ASSERT(rc == 0);
 
     /*** One 16-bit UUID. */
@@ -146,7 +145,8 @@ TEST_CASE(ble_att_clt_test_rx_find_info)
     htole16(buf + off, 0x000f);
     off += 2;
 
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, off);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, off);
     TEST_ASSERT(rc == 0);
 
     /*** Two 16-bit UUIDs. */
@@ -166,7 +166,8 @@ TEST_CASE(ble_att_clt_test_rx_find_info)
     htole16(buf + off, 0x0011);
     off += 2;
 
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, off);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, off);
     TEST_ASSERT(rc == 0);
 }
 
@@ -174,16 +175,15 @@ static void
 ble_att_clt_test_case_tx_write_req_or_cmd(int is_req)
 {
     struct ble_att_write_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t value300[500] = { 0 };
     uint8_t value5[5] = { 6, 7, 54, 34, 8 };
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** 5-byte write. */
     req.bawq_handle = 0x1234;
-    ble_att_clt_test_tx_write_req_or_cmd(conn->bhc_handle, &req, value5,
+    ble_att_clt_test_tx_write_req_or_cmd(conn_handle, &req, value5,
                                          sizeof value5, is_req);
     ble_hs_test_util_tx_all();
     ble_att_clt_test_misc_verify_tx_write(0x1234, value5, sizeof value5,
@@ -191,7 +191,7 @@ ble_att_clt_test_case_tx_write_req_or_cmd(int is_req)
 
     /*** Overlong write; verify command truncated to ATT MTU. */
     req.bawq_handle = 0xab83;
-    ble_att_clt_test_tx_write_req_or_cmd(conn->bhc_handle, &req, value300,
+    ble_att_clt_test_tx_write_req_or_cmd(conn_handle, &req, value300,
                                          sizeof value300, is_req);
     ble_hs_test_util_tx_all();
     ble_att_clt_test_misc_verify_tx_write(0xab83, value300,
@@ -203,17 +203,16 @@ ble_att_clt_test_misc_prep_good(uint16_t handle, uint16_t offset,
                                 uint8_t *attr_data, uint16_t attr_data_len)
 {
     struct ble_att_prep_write_cmd req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
     struct os_mbuf *om;
+    uint16_t conn_handle;
     int rc;
     int i;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     req.bapc_handle = handle;
     req.bapc_offset = offset;
-    rc = ble_att_clt_tx_prep_write(conn->bhc_handle, &req, attr_data,
+    rc = ble_att_clt_tx_prep_write(conn_handle, &req, attr_data,
                                    attr_data_len);
     TEST_ASSERT(rc == 0);
 
@@ -235,15 +234,14 @@ static void
 ble_att_clt_test_misc_exec_good(uint8_t flags)
 {
     struct ble_att_exec_write_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
     struct os_mbuf *om;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     req.baeq_flags = flags;
-    rc = ble_att_clt_tx_exec_write(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_exec_write(conn_handle, &req);
     TEST_ASSERT(rc == 0);
 
     ble_hs_test_util_tx_all();
@@ -261,15 +259,14 @@ ble_att_clt_test_misc_prep_bad(uint16_t handle, uint16_t offset,
                                int status)
 {
     struct ble_att_prep_write_cmd req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     req.bapc_handle = handle;
     req.bapc_offset = offset;
-    rc = ble_att_clt_tx_prep_write(conn->bhc_handle, &req, attr_data,
+    rc = ble_att_clt_tx_prep_write(conn_handle, &req, attr_data,
                                    attr_data_len);
     TEST_ASSERT(rc == status);
 }
@@ -283,104 +280,105 @@ TEST_CASE(ble_att_clt_test_tx_write)
 TEST_CASE(ble_att_clt_test_tx_read)
 {
     struct ble_att_read_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Success. */
     req.barq_handle = 1;
-    rc = ble_att_clt_tx_read(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_read(conn_handle, &req);
     TEST_ASSERT(rc == 0);
 
     /*** Error: handle of 0. */
     req.barq_handle = 0;
-    rc = ble_att_clt_tx_read(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_read(conn_handle, &req);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 }
 
 TEST_CASE(ble_att_clt_test_rx_read)
 {
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t buf[1024];
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Basic success. */
     buf[0] = BLE_ATT_OP_READ_RSP;
     buf[1] = 0;
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 2);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 2);
     TEST_ASSERT(rc == 0);
 
     /*** Larger response. */
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 20);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 20);
     TEST_ASSERT(rc == 0);
 
     /*** Zero-length response. */
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 1);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 1);
     TEST_ASSERT(rc == 0);
 }
 
 TEST_CASE(ble_att_clt_test_tx_read_blob)
 {
     struct ble_att_read_blob_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Success. */
     req.babq_handle = 1;
     req.babq_offset = 0;
-    rc = ble_att_clt_tx_read_blob(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_read_blob(conn_handle, &req);
     TEST_ASSERT(rc == 0);
 
     /*** Error: handle of 0. */
     req.babq_handle = 0;
     req.babq_offset = 0;
-    rc = ble_att_clt_tx_read_blob(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_read_blob(conn_handle, &req);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 }
 
 TEST_CASE(ble_att_clt_test_rx_read_blob)
 {
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t buf[1024];
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Basic success. */
     buf[0] = BLE_ATT_OP_READ_BLOB_RSP;
     buf[1] = 0;
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 2);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 2);
     TEST_ASSERT(rc == 0);
 
     /*** Larger response. */
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 20);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 20);
     TEST_ASSERT(rc == 0);
 
     /*** Zero-length response. */
-    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, 1);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn_handle, BLE_L2CAP_CID_ATT,
+                                                buf, 1);
     TEST_ASSERT(rc == 0);
 }
 
 TEST_CASE(ble_att_clt_test_tx_read_mult)
 {
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
     struct os_mbuf *om;
+    uint16_t conn_handle;
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Success. */
-    rc = ble_att_clt_tx_read_mult(conn->bhc_handle, ((uint16_t[]){ 1, 2 }), 2);
+    rc = ble_att_clt_tx_read_mult(conn_handle, ((uint16_t[]){ 1, 2 }), 2);
     TEST_ASSERT(rc == 0);
 
     ble_hs_test_util_tx_all();
@@ -393,25 +391,24 @@ TEST_CASE(ble_att_clt_test_tx_read_mult)
     TEST_ASSERT(le16toh(om->om_data + BLE_ATT_READ_MULT_REQ_BASE_SZ + 2) == 2);
 
     /*** Error: no handles. */
-    rc = ble_att_clt_tx_read_mult(conn->bhc_handle, NULL, 0);
+    rc = ble_att_clt_tx_read_mult(conn_handle, NULL, 0);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 }
 
 TEST_CASE(ble_att_clt_test_rx_read_mult)
 {
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t buf[1024];
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Basic success. */
     ble_att_read_mult_rsp_write(buf, sizeof buf);
     htole16(buf + BLE_ATT_READ_MULT_RSP_BASE_SZ + 0, 12);
 
     rc = ble_hs_test_util_l2cap_rx_payload_flat(
-        conn, chan, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 2);
+        conn_handle, BLE_L2CAP_CID_ATT, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 2);
     TEST_ASSERT(rc == 0);
 
     /*** Larger response. */
@@ -419,12 +416,12 @@ TEST_CASE(ble_att_clt_test_rx_read_mult)
     htole16(buf + BLE_ATT_READ_MULT_RSP_BASE_SZ + 2, 43);
     htole16(buf + BLE_ATT_READ_MULT_RSP_BASE_SZ + 4, 91);
     rc = ble_hs_test_util_l2cap_rx_payload_flat(
-        conn, chan, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 6);
+        conn_handle, BLE_L2CAP_CID_ATT, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 6);
     TEST_ASSERT(rc == 0);
 
     /*** Zero-length response. */
     rc = ble_hs_test_util_l2cap_rx_payload_flat(
-        conn, chan, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 0);
+        conn_handle, BLE_L2CAP_CID_ATT, buf, BLE_ATT_READ_MULT_RSP_BASE_SZ + 0);
     TEST_ASSERT(rc == 0);
 }
 
@@ -462,12 +459,11 @@ TEST_CASE(ble_att_clt_test_tx_prep_write)
 TEST_CASE(ble_att_clt_test_rx_prep_write)
 {
     struct ble_att_prep_write_cmd rsp;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     uint8_t buf[1024];
     int rc;
 
-    ble_att_clt_test_misc_init(&conn, &chan);
+    conn_handle = ble_att_clt_test_misc_init();
 
     /*** Basic success. */
     rsp.bapc_handle = 0x1234;
@@ -475,7 +471,8 @@ TEST_CASE(ble_att_clt_test_rx_prep_write)
     ble_att_prep_write_rsp_write(buf, sizeof buf, &rsp);
     memset(buf + BLE_ATT_PREP_WRITE_CMD_BASE_SZ, 1, 5);
     rc = ble_hs_test_util_l2cap_rx_payload_flat(
-        conn, chan, buf, BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 5);
+        conn_handle, BLE_L2CAP_CID_ATT, buf,
+        BLE_ATT_PREP_WRITE_CMD_BASE_SZ + 5);
     TEST_ASSERT(rc == 0);
 
     /*** 0-length write. */
@@ -483,25 +480,25 @@ TEST_CASE(ble_att_clt_test_rx_prep_write)
     rsp.bapc_offset = 0;
     ble_att_prep_write_rsp_write(buf, sizeof buf, &rsp);
     rc = ble_hs_test_util_l2cap_rx_payload_flat(
-        conn, chan, buf, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
+        conn_handle, BLE_L2CAP_CID_ATT, buf, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
     TEST_ASSERT(rc == 0);
 }
 
 TEST_CASE(ble_att_clt_test_tx_exec_write)
 {
     struct ble_att_exec_write_req req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
+    uint16_t conn_handle;
     int rc;
 
+    conn_handle = ble_att_clt_test_misc_init();
+
     /*** Success. */
     ble_att_clt_test_misc_exec_good(0);
     ble_att_clt_test_misc_exec_good(BLE_ATT_EXEC_WRITE_F_CONFIRM);
 
     /*** Error: invalid flags value. */
-    ble_att_clt_test_misc_init(&conn, &chan);
     req.baeq_flags = 0x02;
-    rc = ble_att_clt_tx_exec_write(conn->bhc_handle, &req);
+    rc = ble_att_clt_tx_exec_write(conn_handle, &req);
     TEST_ASSERT(rc == BLE_HS_EINVAL);
 }