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 2015/11/06 04:15:50 UTC

[1/2] incubator-mynewt-larva git commit: Fix os_mbuf_append() bug - data was overwritten.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 05bf9f230 -> dfd27cb24


Fix os_mbuf_append() bug - data was overwritten.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/commit/6f85430f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/6f85430f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/6f85430f

Branch: refs/heads/master
Commit: 6f85430f50ce40c2db51f924c62e2f13ccff5a29
Parents: 05bf9f2
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Nov 5 19:15:21 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Nov 5 19:15:21 2015 -0800

----------------------------------------------------------------------
 libs/os/src/os_mbuf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/6f85430f/libs/os/src/os_mbuf.c
----------------------------------------------------------------------
diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c
index 6e3b745..d6710d3 100644
--- a/libs/os/src/os_mbuf.c
+++ b/libs/os/src/os_mbuf.c
@@ -232,7 +232,7 @@ os_mbuf_append(struct os_mbuf_pool *omp, struct os_mbuf *om, void *data,
             space = remainder;
         }
 
-        memcpy(OS_MBUF_DATA(last, void *), data, space);
+        memcpy(OS_MBUF_DATA(last, void *) + last->om_len , data, space);
 
         last->om_len += space;
         data += space;


[2/2] incubator-mynewt-larva git commit: Add some rudimentary att-read-req handling.

Posted by cc...@apache.org.
Add some rudimentary att-read-req handling.


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

Branch: refs/heads/master
Commit: dfd27cb24b27de8a79c1ecd86496ef7fb1dde269
Parents: 6f85430
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Nov 5 19:15:41 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Nov 5 19:15:41 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/src/ble_hs_att.c           | 184 ++++++++++++++++++------
 net/nimble/host/src/ble_hs_att.h           |  25 ++--
 net/nimble/host/src/ble_hs_att_cmd.h       |  29 ++--
 net/nimble/host/src/ble_l2cap.c            |   2 -
 net/nimble/host/src/test/ble_hs_att_test.c |  65 ++++++++-
 5 files changed, 238 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dfd27cb2/net/nimble/host/src/ble_hs_att.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att.c b/net/nimble/host/src/ble_hs_att.c
index e8a80ae..f30b3e3 100644
--- a/net/nimble/host/src/ble_hs_att.c
+++ b/net/nimble/host/src/ble_hs_att.c
@@ -43,11 +43,15 @@ struct ble_hs_att_rx_dispatch_entry ble_hs_att_rx_dispatch[] = {
 #define BLE_HS_ATT_RX_DISPATCH_SZ \
     (sizeof ble_hs_att_rx_dispatch / sizeof ble_hs_att_rx_dispatch[0])
 
-static STAILQ_HEAD(, host_attr) g_host_attr_list = 
-    STAILQ_HEAD_INITIALIZER(g_host_attr_list);
-static uint16_t g_host_attr_id;
+static STAILQ_HEAD(, ble_hs_att_entry) g_ble_hs_att_list =
+    STAILQ_HEAD_INITIALIZER(g_ble_hs_att_list);
+static uint16_t g_ble_hs_att_id;
 
-static struct os_mutex g_host_attr_list_mutex;
+static struct os_mutex g_ble_hs_att_list_mutex;
+
+#define BLE_HS_ATT_NUM_ENTRIES          1024
+static void *ble_hs_att_entry_mem;
+static struct os_mempool ble_hs_att_entry_pool;
 
 /**
  * Lock the host attribute list.
@@ -55,11 +59,11 @@ static struct os_mutex g_host_attr_list_mutex;
  * @return 0 on success, non-zero error code on failure.
  */
 static int 
-host_attr_list_lock(void)
+ble_hs_att_list_lock(void)
 {
     int rc;
 
-    rc = os_mutex_pend(&g_host_attr_list_mutex, OS_WAIT_FOREVER);
+    rc = os_mutex_pend(&g_ble_hs_att_list_mutex, OS_WAIT_FOREVER);
     if (rc != 0 && rc != OS_NOT_STARTED) {
         goto err;
     }
@@ -75,11 +79,11 @@ err:
  * @return 0 on success, non-zero error code on failure.
  */
 static int 
-host_attr_list_unlock(void)
+ble_hs_att_list_unlock(void)
 {
     int rc;
 
-    rc = os_mutex_release(&g_host_attr_list_mutex);
+    rc = os_mutex_release(&g_ble_hs_att_list_mutex);
     if (rc != 0 && rc != OS_NOT_STARTED) {
         goto err;
     }
@@ -89,6 +93,29 @@ err:
     return (rc);
 }
 
+static struct ble_hs_att_entry *
+ble_hs_att_entry_alloc(void)
+{
+    struct ble_hs_att_entry *entry;
+
+    entry = os_memblock_get(&ble_hs_att_entry_pool);
+    if (entry != NULL) {
+        memset(entry, 0, sizeof *entry);
+    }
+
+    return entry;
+}
+
+#if 0
+static void
+ble_hs_att_entry_free(struct ble_hs_att_entry *entry)
+{
+    int rc;
+
+    rc = os_memblock_put(&ble_hs_att_entry_pool, entry);
+    assert(rc == 0);
+}
+#endif
 
 /**
  * Allocate the next handle id and return it.
@@ -96,41 +123,55 @@ err:
  * @return A new 16-bit handle ID.
  */
 static uint16_t
-host_attr_next_id(void)
+ble_hs_att_next_id(void)
 {
-    return (++g_host_attr_id);
+    return (++g_ble_hs_att_id);
 }
 
 /**
  * Register a host attribute with the BLE stack.
  *
- * @param ha A filled out host_attr structure to register
+ * @param ha A filled out ble_hs_att structure to register
  * @param handle_id A pointer to a 16-bit handle ID, which will be the 
  *                  handle that is allocated.
  *
  * @return 0 on success, non-zero error code on failure.
  */
 int 
-host_attr_register(struct host_attr *ha, uint16_t *handle_id)
+ble_hs_att_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
+                    ble_hs_att_handle_func *fn)
 {
+    struct ble_hs_att_entry *entry;
     int rc;
 
-    *handle_id = host_attr_next_id();
-    ha->ha_handle_id = *handle_id;
+    entry = ble_hs_att_entry_alloc();
+    if (entry == NULL) {
+        return ENOMEM;
+    }
+
+    memcpy(&entry->ha_uuid, uuid, sizeof entry->ha_uuid);
+    entry->ha_flags = flags;
+    entry->ha_handle_id = ble_hs_att_next_id();
+    entry->ha_fn = fn;
 
-    rc = host_attr_list_lock();
+    rc = ble_hs_att_list_lock();
     if (rc != 0) {
         goto err;
     }
 
-    STAILQ_INSERT_TAIL(&g_host_attr_list, ha, ha_next);
+    STAILQ_INSERT_TAIL(&g_ble_hs_att_list, entry, ha_next);
 
-    rc = host_attr_list_unlock();
+    rc = ble_hs_att_list_unlock();
     if (rc != 0) {
         goto err;
     }
 
+    if (handle_id != NULL) {
+        *handle_id = entry->ha_handle_id;
+    }
+
     return (0);
+
 err:
     return (rc);
 }
@@ -144,29 +185,29 @@ err:
  *                  list.
  * @param arg       The argument to provide to walk_func
  * @param ha_ptr    A pointer to a pointer which will be set to the last 
- *                  host_attr element processed, or NULL if the entire list has 
+ *                  ble_hs_att element processed, or NULL if the entire list has 
  *                  been processed
  *
  * @return 1 on stopped, 0 on fully processed and an error code otherwise.
  */
 int
-host_attr_walk(host_attr_walk_func_t walk_func, void *arg, 
-        struct host_attr **ha_ptr)
+ble_hs_att_walk(ble_hs_att_walk_func_t walk_func, void *arg, 
+        struct ble_hs_att_entry **ha_ptr)
 {
-    struct host_attr *ha;
+    struct ble_hs_att_entry *ha;
     int rc;
 
-    rc = host_attr_list_lock();
+    rc = ble_hs_att_list_lock();
     if (rc != 0) {
         goto err;
     }
 
     *ha_ptr = NULL;
     ha = NULL;
-    STAILQ_FOREACH(ha, &g_host_attr_list, ha_next) {
+    STAILQ_FOREACH(ha, &g_ble_hs_att_list, ha_next) {
         rc = walk_func(ha, arg);
         if (rc == 1) {
-            rc = host_attr_list_unlock();
+            rc = ble_hs_att_list_unlock();
             if (rc != 0) {
                 goto err;
             }
@@ -175,7 +216,7 @@ host_attr_walk(host_attr_walk_func_t walk_func, void *arg,
         }
     }
 
-    rc = host_attr_list_unlock();
+    rc = ble_hs_att_list_unlock();
     if (rc != 0) {
         goto err;
     }
@@ -186,7 +227,7 @@ err:
 }
 
 static int 
-host_attr_match_handle(struct host_attr *ha, void *arg)
+ble_hs_att_match_handle(struct ble_hs_att_entry *ha, void *arg)
 {
     if (ha->ha_handle_id == *(uint16_t *) arg) {
         return (1);
@@ -200,17 +241,17 @@ host_attr_match_handle(struct host_attr *ha, void *arg)
  * Find a host attribute by handle id. 
  *
  * @param handle_id The handle_id to search for
- * @param host_attr A pointer to a pointer to put the matching host attr into.
+ * @param ble_hs_att A pointer to a pointer to put the matching host attr into.
  *
  * @return 0 on success, BLE_ERR_ATTR_NOT_FOUND on not found, and non-zero on 
  *         error.
  */
 int
-host_attr_find_by_handle(uint16_t handle_id, struct host_attr **ha_ptr)
+ble_hs_att_find_by_handle(uint16_t handle_id, struct ble_hs_att_entry **ha_ptr)
 {
     int rc;
 
-    rc = host_attr_walk(host_attr_match_handle, &handle_id, ha_ptr);
+    rc = ble_hs_att_walk(ble_hs_att_match_handle, &handle_id, ha_ptr);
     if (rc == 1) {
         /* Found a matching handle */
         return (0);
@@ -223,7 +264,7 @@ host_attr_find_by_handle(uint16_t handle_id, struct host_attr **ha_ptr)
 }
 
 static int 
-host_attr_match_uuid(struct host_attr *ha, void *arg)
+ble_hs_att_match_uuid(struct ble_hs_att_entry *ha, void *arg)
 {
     ble_uuid_t *uuid;
 
@@ -236,7 +277,6 @@ host_attr_match_uuid(struct host_attr *ha, void *arg)
     }
 }
 
-
 /**
  * Find a host attribute by UUID.
  *
@@ -247,11 +287,11 @@ host_attr_match_uuid(struct host_attr *ha, void *arg)
  *         error.
  */
 int
-host_attr_find_by_uuid(ble_uuid_t *uuid, struct host_attr **ha_ptr) 
+ble_hs_att_find_by_uuid(ble_uuid_t *uuid, struct ble_hs_att_entry **ha_ptr) 
 {
     int rc;
     
-    rc = host_attr_walk(host_attr_match_uuid, uuid, ha_ptr);
+    rc = ble_hs_att_walk(ble_hs_att_match_uuid, uuid, ha_ptr);
     if (rc == 1) {
         /* Found a matching handle */
         return (0);
@@ -308,11 +348,37 @@ ble_hs_att_tx_error_rsp(struct ble_l2cap_chan *chan, uint8_t req_op,
 }
 
 static int
+ble_hs_att_tx_read_rsp(struct ble_l2cap_chan *chan, void *data, int data_len)
+{
+    uint8_t op;
+    int rc;
+
+    op = BLE_HS_ATT_OP_READ_RSP;
+    rc = ble_l2cap_tx(chan, &op, 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* XXX: Check attribute length against MTU. */
+
+    rc = ble_l2cap_tx(chan, data, data_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* XXX: Kick L2CAP. */
+
+    return 0;
+}
+
+static int
 ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
 {
     struct ble_hs_att_read_req req;
-    struct host_attr *attr;
+    struct ble_hs_att_entry *entry;
     uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
+    uint8_t *attr_data;
+    int attr_len;
     int rc;
 
     rc = os_mbuf_copydata(chan->blc_rx_buf, 0, sizeof buf, buf);
@@ -325,16 +391,33 @@ ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
     rc = ble_hs_att_read_req_parse(buf, sizeof buf, &req);
     assert(rc == 0);
 
-    rc = host_attr_find_by_handle(req.bharq_handle, &attr);
+    rc = ble_hs_att_find_by_handle(req.bharq_handle, &entry);
     if (rc != 0) {
-        ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_READ_REQ,
-                                req.bharq_handle, rc);
-        return rc;
+        goto send_err;
     }
 
-    /* XXX: Send response. */
+    if (entry->ha_fn == NULL) {
+        rc = BLE_ERR_UNSPECIFIED;
+        goto send_err;
+    }
+
+    rc = entry->ha_fn(entry, BLE_HS_ATT_OP_READ_REQ, &attr_data, &attr_len);
+    if (rc != 0) {
+        rc = BLE_ERR_UNSPECIFIED;
+        goto send_err;
+    }
+
+    rc = ble_hs_att_tx_read_rsp(chan, attr_data, attr_len);
+    if (rc != 0) {
+        goto send_err;
+    }
 
     return 0;
+
+send_err:
+    ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_READ_REQ,
+                            req.bharq_handle, rc);
+    return rc;
 }
 
 static int
@@ -383,6 +466,25 @@ ble_hs_att_init(void)
 {
     int rc;
 
-    rc = os_mutex_init(&g_host_attr_list_mutex);
-    return rc;
+    rc = os_mutex_init(&g_ble_hs_att_list_mutex);
+    if (rc != 0) {
+        return rc;
+    }
+
+    free(ble_hs_att_entry_mem);
+    ble_hs_att_entry_mem = malloc(
+        OS_MEMPOOL_BYTES(BLE_HS_ATT_NUM_ENTRIES,
+                         sizeof (struct ble_hs_att_entry)));
+    if (ble_hs_att_entry_mem == NULL) {
+        return ENOMEM;
+    }
+
+    rc = os_mempool_init(&ble_hs_att_entry_pool, BLE_HS_ATT_NUM_ENTRIES,
+                         sizeof (struct ble_hs_att_entry),
+                         ble_hs_att_entry_mem, "ble_hs_att_entry_pool");
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dfd27cb2/net/nimble/host/src/ble_hs_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att.h b/net/nimble/host/src/ble_hs_att.h
index 47d821f..7ab8c8c 100644
--- a/net/nimble/host/src/ble_hs_att.h
+++ b/net/nimble/host/src/ble_hs_att.h
@@ -21,26 +21,27 @@
 
 #define BLE_HOST_EVENT_NEW_ATTR_CONN (OS_EVENT_T_PERUSER)
 
-struct host_attr;
+struct ble_hs_att_entry;
 
 /**
- * Called from host_attr_walk().  Called on each entry in the 
- * host_attr_list.
+ * Called from ble_hs_att_walk().  Called on each entry in the 
+ * ble_hs_att_list.
  *
- * @param Contains the current host_attr being iterated through
- * @param The user supplied argument to host_attr_walk()
+ * @param Contains the current ble_hs_att being iterated through
+ * @param The user supplied argument to ble_hs_att_walk()
  *
  * @return 0 on continue, 1 on stop
  */
-typedef int (*host_attr_walk_func_t)(struct host_attr *, void *arg);
+typedef int (*ble_hs_att_walk_func_t)(struct ble_hs_att_entry *, void *arg);
 
 /**
  * Handles a host attribute request.
  *
- * @param The host attribute being requested 
+ * @param entry The host attribute being requested
  * @param The request data associated with that host attribute
  */
-typedef int (*host_attr_handle_func_t)(struct host_attr *, uint8_t *data);
+typedef int ble_hs_att_handle_func(struct ble_hs_att_entry *entry,
+                                   uint8_t op, uint8_t **data, int *len);
 
 #define HA_FLAG_PERM_READ            (1 << 0)
 #define HA_FLAG_PERM_WRITE           (1 << 1) 
@@ -49,12 +50,14 @@ typedef int (*host_attr_handle_func_t)(struct host_attr *, uint8_t *data);
 #define HA_FLAG_AUTHENTICATION_REQ   (1 << 4)
 #define HA_FLAG_AUTHORIZATION_REQ    (1 << 5)
 
-struct host_attr {
+struct ble_hs_att_entry {
+    STAILQ_ENTRY(ble_hs_att_entry) ha_next;
+
     ble_uuid_t ha_uuid;
     uint8_t ha_flags;
     uint8_t ha_pad1;
     uint16_t ha_handle_id;
-    STAILQ_ENTRY(host_attr) ha_next;
+    ble_hs_att_handle_func *ha_fn;
 };
 
 #define HA_OPCODE_METHOD_START (0)
@@ -67,6 +70,8 @@ struct host_attr {
 #define HA_METH_EXCHANGE_MTU_REQ (0x02)
 #define HA_METH_EXCHANGE_MTU_RSP (0x03)
 
+int ble_hs_att_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
+                        ble_hs_att_handle_func *fn);
 struct ble_l2cap_chan *ble_hs_att_create_chan(void);
 int ble_hs_att_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dfd27cb2/net/nimble/host/src/ble_hs_att_cmd.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att_cmd.h b/net/nimble/host/src/ble_hs_att_cmd.h
index 5c139f1..a06e75f 100644
--- a/net/nimble/host/src/ble_hs_att_cmd.h
+++ b/net/nimble/host/src/ble_hs_att_cmd.h
@@ -19,12 +19,13 @@ struct ble_l2cap_chan;
 
 #define BLE_HS_ATT_OP_ERROR_RSP     0x01
 #define BLE_HS_ATT_OP_READ_REQ      0x0a
+#define BLE_HS_ATT_OP_READ_RSP      0x0b
 
 /**
- * | Parameter                          | Size (octets) |
- * +------------------------------------+---------------+
- * | Attribute Opcode                   | 1             |
- * | Attribute Handle                   | 2             |
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Attribute Handle                   | 2                 |
  */
 #define BLE_HS_ATT_READ_REQ_SZ      3
 struct ble_hs_att_read_req {
@@ -33,12 +34,20 @@ struct ble_hs_att_read_req {
 };
 
 /**
- * | Parameter                          | Size (octets) |
- * +------------------------------------+---------------+
- * | Attribute Opcode                   | 1             |
- * | Request Opcode In Error            | 1             |
- * | Attribute Handle In Error          | 2             |
- * | Error Code                         | 1             |
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Attribute Value                    | 0 to (ATT_MTU-1)  |
+ */
+#define BLE_HS_ATT_READ_RSP_MIN_SZ      1
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Request Opcode In Error            | 1                 |
+ * | Attribute Handle In Error          | 2                 |
+ * | Error Code                         | 1                 |
  */
 #define BLE_HS_ATT_ERROR_RSP_SZ     5
 struct ble_hs_att_error_rsp {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dfd27cb2/net/nimble/host/src/ble_l2cap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap.c b/net/nimble/host/src/ble_l2cap.c
index cdacda9..9c85c8c 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -224,8 +224,6 @@ ble_l2cap_tx(struct ble_l2cap_chan *chan, void *payload, int len)
         return rc;
     }
 
-    /* XXX: L2CAP kick. */
-
     return 0;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/dfd27cb2/net/nimble/host/src/test/ble_hs_att_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_att_test.c b/net/nimble/host/src/test/ble_hs_att_test.c
index 0243ffd..72bdae0 100644
--- a/net/nimble/host/src/test/ble_hs_att_test.c
+++ b/net/nimble/host/src/test/ble_hs_att_test.c
@@ -21,13 +21,26 @@
 #include "host/ble_hs_test.h"
 #include "ble_l2cap.h"
 #include "ble_hs_conn.h"
+#include "ble_hs_att.h"
 #include "ble_hs_att_cmd.h"
 #include "testutil/testutil.h"
 
+static uint8_t ble_hs_att_test_attr_1[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+
+static int
+ble_hs_att_test_misc_attr_fn_1(struct ble_hs_att_entry *entry,
+                               uint8_t op, uint8_t **data, int *len)
+{
+    *data = ble_hs_att_test_attr_1;
+    *len = sizeof ble_hs_att_test_attr_1;
+
+    return 0;
+}
+
 static void
-ble_hs_att_test_misc_verify_err_rsp(struct ble_l2cap_chan *chan,
-                                    uint8_t req_op, uint16_t handle,
-                                    uint8_t error_code)
+ble_hs_att_test_misc_verify_tx_err_rsp(struct ble_l2cap_chan *chan,
+                                       uint8_t req_op, uint16_t handle,
+                                       uint8_t error_code)
 {
     struct ble_hs_att_error_rsp rsp;
     uint8_t buf[BLE_HS_ATT_ERROR_RSP_SZ];
@@ -43,6 +56,35 @@ ble_hs_att_test_misc_verify_err_rsp(struct ble_l2cap_chan *chan,
     TEST_ASSERT(rsp.bhaep_req_op == req_op);
     TEST_ASSERT(rsp.bhaep_handle == handle);
     TEST_ASSERT(rsp.bhaep_error_code == error_code);
+
+    /* Remove the error response from the buffer. */
+    os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf,
+                BLE_HS_ATT_ERROR_RSP_SZ);
+}
+
+static void
+ble_hs_att_test_misc_verify_tx_read_rsp(struct ble_l2cap_chan *chan,
+                                        uint8_t *attr_data, int attr_len)
+{
+    uint8_t u8;
+    int rc;
+    int i;
+
+    rc = os_mbuf_copydata(chan->blc_tx_buf, 0, 1, &u8);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(u8 == BLE_HS_ATT_OP_READ_RSP);
+
+    for (i = 0; i < attr_len; i++) {
+        rc = os_mbuf_copydata(chan->blc_tx_buf, i + 1, 1, &u8);
+        TEST_ASSERT(rc == 0);
+        TEST_ASSERT(u8 == attr_data[i]);
+    }
+
+    rc = os_mbuf_copydata(chan->blc_tx_buf, i + 1, 1, &u8);
+    TEST_ASSERT(rc != 0);
+
+    /* Remove the read response from the buffer. */
+    os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, i);
 }
 
 TEST_CASE(ble_hs_att_test_small_read)
@@ -51,6 +93,7 @@ TEST_CASE(ble_hs_att_test_small_read)
     struct ble_l2cap_chan *chan;
     struct ble_hs_conn *conn;
     uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
+    uint8_t uuid[16] = {0};
     int rc;
 
     conn = ble_hs_conn_alloc();
@@ -67,9 +110,23 @@ TEST_CASE(ble_hs_att_test_small_read)
 
     rc = ble_l2cap_rx_payload(conn, chan, buf, sizeof buf);
     TEST_ASSERT(rc != 0);
-    ble_hs_att_test_misc_verify_err_rsp(chan, BLE_HS_ATT_OP_READ_REQ, 0,
+    ble_hs_att_test_misc_verify_tx_err_rsp(chan, BLE_HS_ATT_OP_READ_REQ, 0,
                                         BLE_ERR_ATTR_NOT_FOUND);
 
+    /*** Successful read. */
+    rc = ble_hs_att_register(uuid, 0, &req.bharq_handle,
+                             ble_hs_att_test_misc_attr_fn_1);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_hs_att_read_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_l2cap_rx_payload(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+
+    ble_hs_att_test_misc_verify_tx_read_rsp(chan, ble_hs_att_test_attr_1,
+                                            sizeof ble_hs_att_test_attr_1);
+
     ble_hs_conn_free(conn);
 }