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/20 03:09:59 UTC

incubator-mynewt-larva git commit: att find type value command.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 0c08cbb69 -> 34a6d69f2


att find type value command.


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/34a6d69f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/tree/34a6d69f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/diff/34a6d69f

Branch: refs/heads/master
Commit: 34a6d69f282276e496e45df25bce90081b02a511
Parents: 0c08cbb
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Nov 19 18:09:39 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Nov 19 18:09:39 2015 -0800

----------------------------------------------------------------------
 libs/os/include/os/os_mbuf.h                |   4 +
 libs/os/src/os_mbuf.c                       |  86 ++++
 net/nimble/host/src/ble_hs_att.c            | 538 ++++++++++++++++++-----
 net/nimble/host/src/ble_hs_att.h            |   2 +
 net/nimble/host/src/ble_hs_uuid.c           |  10 +-
 net/nimble/host/src/ble_hs_uuid.h           |   2 +-
 net/nimble/host/src/ble_l2cap.c             |  56 ++-
 net/nimble/host/src/ble_l2cap.h             |   3 +-
 net/nimble/host/src/test/ble_hs_att_test.c  | 331 ++++++++++++--
 net/nimble/host/src/test/ble_hs_uuid_test.c |   8 +-
 10 files changed, 893 insertions(+), 147 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/libs/os/include/os/os_mbuf.h
----------------------------------------------------------------------
diff --git a/libs/os/include/os/os_mbuf.h b/libs/os/include/os/os_mbuf.h
index a4d4075..289e739 100644
--- a/libs/os/include/os/os_mbuf.h
+++ b/libs/os/include/os/os_mbuf.h
@@ -205,6 +205,8 @@ struct os_mbuf *os_mbuf_get_pkthdr(struct os_mbuf_pool *omp);
 /* Duplicate a mbuf from the pool */
 struct os_mbuf *os_mbuf_dup(struct os_mbuf_pool *omp, struct os_mbuf *m);
 
+struct os_mbuf * os_mbuf_off(struct os_mbuf *om, int off, int *out_off);
+
 /* Copy data from an mbuf to a flat buffer. */
 int os_mbuf_copydata(const struct os_mbuf *m, int off, int len, void *dst);
 
@@ -219,6 +221,8 @@ int os_mbuf_free(struct os_mbuf_pool *omp, struct os_mbuf *mb);
 int os_mbuf_free_chain(struct os_mbuf_pool *omp, struct os_mbuf *om);
 
 void os_mbuf_adj(struct os_mbuf_pool *omp, struct os_mbuf *mp, int req_len);
+int os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data,
+                   int len);
 
 
 #endif /* _OS_MBUF_H */ 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/libs/os/src/os_mbuf.c
----------------------------------------------------------------------
diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c
index f78d373..75f9074 100644
--- a/libs/os/src/os_mbuf.c
+++ b/libs/os/src/os_mbuf.c
@@ -326,6 +326,37 @@ err:
     return (NULL);
 }
 
+/**
+ * Locates the specified absolute offset within an mbuf chain.  The offset
+ * can be one past than the total length of the chain, but no greater.
+ *
+ * @param om                    The start of the mbuf chain to seek within.
+ * @param off                   The absolute address to find.
+ * @param out_off               On success, this points to the relative offset
+ *                                  within the returned mbuf.
+ *
+ * @return                      The mbuf containing the specified offset on
+ *                                  success.
+ *                              NULL if the specified offset is out of bounds.
+ */
+struct os_mbuf *
+os_mbuf_off(struct os_mbuf *om, int off, int *out_off)
+{
+    while (1) {
+        if (om == NULL) {
+            return NULL;
+        }
+
+        if (om->om_len >= off) {
+            *out_off = off;
+            return om;
+        }
+
+        off -= om->om_len;
+        om = SLIST_NEXT(om, om_next);
+    }
+}
+
 /*
  * Copy data from an mbuf chain starting "off" bytes from the beginning,
  * continuing for "len" bytes, into the indicated buffer.
@@ -436,6 +467,61 @@ os_mbuf_adj(struct os_mbuf_pool *omp, struct os_mbuf *mp, int req_len)
     }
 }
 
+/**
+ * Performs a memory compare of the specified region of an mbuf chain against a
+ * flat buffer.
+ *
+ * @param om                    The start of the mbuf chain to compare.
+ * @param off                   The offset within the mbuf chain to start the
+ *                                  comparison.
+ * @param data                  The flat buffer to compare.
+ * @param len                   The length of the flat buffer.
+ *
+ * @return                      0 if both memory regions are identical;
+ *                              A memcmp return code if there is a mismatch;
+ *                              -1 if the mbuf is too short.
+ */
+int
+os_mbuf_memcmp(const struct os_mbuf *om, int off, const void *data, int len)
+{
+    int chunk_sz;
+    int data_off;
+    int om_off;
+    int rc;
+
+    if (len <= 0) {
+        return 0;
+    }
+
+    data_off = 0;
+    om = os_mbuf_off((struct os_mbuf *)om, off, &om_off);
+    while (1) {
+        if (om == NULL) {
+            return -1;
+        }
+
+        chunk_sz = min(om->om_len - om_off, len - data_off);
+        if (chunk_sz > 0) {
+            rc = memcmp(om->om_data + om_off, data + data_off, chunk_sz);
+            if (rc != 0) {
+                return rc;
+            }
+        }
+
+        data_off += chunk_sz;
+        if (data_off == len) {
+            return 0;
+        }
+
+        om = SLIST_NEXT(om, om_next);
+        om_off = 0;
+
+        if (om == NULL) {
+            return -1;
+        }
+    }
+}
+
 
 #if 0
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/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 4e17551..02fc2a2 100644
--- a/net/nimble/host/src/ble_hs_att.c
+++ b/net/nimble/host/src/ble_hs_att.c
@@ -26,8 +26,6 @@
 #include "ble_hs_att_cmd.h"
 #include "ble_hs_att.h"
 
-static uint8_t *ble_hs_att_tx_buf;
-
 typedef int ble_hs_att_rx_fn(struct ble_hs_conn *conn,
                              struct ble_l2cap_chan *chan,
                              struct os_mbuf *om);
@@ -43,6 +41,9 @@ static int ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn,
 static int ble_hs_att_rx_find_info_req(struct ble_hs_conn *conn,
                                        struct ble_l2cap_chan *chan,
                                        struct os_mbuf *om);
+static int ble_hs_att_rx_find_type_value_req(struct ble_hs_conn *conn,
+                                             struct ble_l2cap_chan *chan,
+                                             struct os_mbuf *om);
 static int ble_hs_att_rx_read_req(struct ble_hs_conn *conn,
                                   struct ble_l2cap_chan *chan,
                                   struct os_mbuf *om);
@@ -51,10 +52,11 @@ static int ble_hs_att_rx_write_req(struct ble_hs_conn *conn,
                                    struct os_mbuf *om);
 
 static struct ble_hs_att_rx_dispatch_entry ble_hs_att_rx_dispatch[] = {
-    { BLE_HS_ATT_OP_MTU_REQ,        ble_hs_att_rx_mtu_req },
-    { BLE_HS_ATT_OP_FIND_INFO_REQ,  ble_hs_att_rx_find_info_req },
-    { BLE_HS_ATT_OP_READ_REQ,       ble_hs_att_rx_read_req },
-    { BLE_HS_ATT_OP_WRITE_REQ,      ble_hs_att_rx_write_req },
+    { BLE_HS_ATT_OP_MTU_REQ,              ble_hs_att_rx_mtu_req },
+    { BLE_HS_ATT_OP_FIND_INFO_REQ,        ble_hs_att_rx_find_info_req },
+    { BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ,  ble_hs_att_rx_find_type_value_req },
+    { BLE_HS_ATT_OP_READ_REQ,             ble_hs_att_rx_read_req },
+    { BLE_HS_ATT_OP_WRITE_REQ,            ble_hs_att_rx_write_req },
 };
 
 #define BLE_HS_ATT_RX_DISPATCH_SZ \
@@ -313,6 +315,7 @@ ble_hs_att_tx_error_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];
     int rc;
 
     rsp.bhaep_op = BLE_HS_ATT_OP_ERROR_RSP;
@@ -320,11 +323,10 @@ ble_hs_att_tx_error_rsp(struct ble_l2cap_chan *chan, uint8_t req_op,
     rsp.bhaep_handle = handle;
     rsp.bhaep_error_code = error_code;
 
-    rc = ble_hs_att_error_rsp_write(ble_hs_att_tx_buf,
-                                    BLE_HS_ATT_ERROR_RSP_SZ, &rsp);
+    rc = ble_hs_att_error_rsp_write(buf, sizeof buf, &rsp);
     assert(rc == 0);
 
-    rc = ble_l2cap_tx(chan, ble_hs_att_tx_buf, BLE_HS_ATT_ERROR_RSP_SZ);
+    rc = ble_l2cap_tx_flat(chan, buf, BLE_HS_ATT_ERROR_RSP_SZ);
     if (rc != 0) {
         return rc;
     }
@@ -336,6 +338,7 @@ static int
 ble_hs_att_tx_mtu_cmd(struct ble_l2cap_chan *chan, uint8_t op, uint16_t mtu)
 {
     struct ble_hs_att_mtu_cmd cmd;
+    uint8_t buf[BLE_HS_ATT_MTU_CMD_SZ];
     int rc;
 
     assert(!(chan->blc_flags & BLE_L2CAP_CHAN_F_TXED_MTU));
@@ -345,11 +348,10 @@ ble_hs_att_tx_mtu_cmd(struct ble_l2cap_chan *chan, uint8_t op, uint16_t mtu)
     cmd.bhamc_op = op;
     cmd.bhamc_mtu = mtu;
 
-    rc = ble_hs_att_mtu_cmd_write(ble_hs_att_tx_buf, BLE_HS_ATT_MTU_MAX,
-                                  &cmd);
+    rc = ble_hs_att_mtu_cmd_write(buf, sizeof buf, &cmd);
     assert(rc == 0);
 
-    rc = ble_l2cap_tx(chan, ble_hs_att_tx_buf, BLE_HS_ATT_MTU_CMD_SZ);
+    rc = ble_l2cap_tx_flat(chan, buf, sizeof buf);
     if (rc != 0) {
         return rc;
     }
@@ -364,6 +366,7 @@ ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                       struct os_mbuf *om)
 {
     struct ble_hs_att_mtu_cmd cmd;
+    uint8_t buf[BLE_HS_ATT_MTU_CMD_SZ];
     int rc;
 
     /* We should only receive this command as a server. */
@@ -372,11 +375,10 @@ ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         return EINVAL;
     }
 
-    rc = os_mbuf_copydata(om, 0, BLE_HS_ATT_MTU_CMD_SZ, ble_hs_att_tx_buf);
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
     assert(rc == 0);
 
-    rc = ble_hs_att_mtu_cmd_parse(ble_hs_att_tx_buf, BLE_HS_ATT_MTU_CMD_SZ,
-                                  &cmd);
+    rc = ble_hs_att_mtu_cmd_parse(buf, sizeof buf, &cmd);
     assert(rc == 0);
 
     if (cmd.bhamc_mtu < BLE_HS_ATT_MTU_DFLT) {
@@ -394,16 +396,14 @@ ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 }
 
 /**
- * Fills the static ATT transmit buffer with the variable length Information
- * Data field of a Find Information ATT response.
+ * Fills the supplied mbuf with the variable length Information Data field of a
+ * Find Information ATT response.
  *
  * @param req                   The Find Information request being responded
  *                                  to.
- * @param buf                   The destination buffer where the Information
+ * @param om                    The destination mbuf where the Information
  *                                  Data field gets written.
- * @param buf_sz                The maximum size of the output data.
- * @param rsp_sz                On success, the number of bytes written gets
- *                                  stored here.
+ * @param mtu                   The ATT L2CAP channel MTU.
  * @param format                On success, the format field of the response
  *                                  gets stored here.  One of:
  *                                     o BLE_HS_ATT_FIND_INFO_RSP_FORMAT_16BIT
@@ -412,27 +412,32 @@ ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
  * @return                      0 on success; an ATT error code on failure.
  */
 static int
-ble_hs_att_fill_info(struct ble_hs_att_find_info_req *req, uint8_t *buf,
-                     int buf_sz, int *rsp_sz, uint8_t *format)
+ble_hs_att_fill_info(struct ble_hs_att_find_info_req *req, struct os_mbuf *om,
+                     uint16_t mtu, uint8_t *format)
 {
     struct ble_hs_att_entry *ha;
-    int new_rsp_sz;
-    int uuid16;
+    uint16_t handle_id;
+    uint16_t uuid16;
+    int num_entries;
+    int rsp_sz;
+    int rc;
 
     *format = 0;
-    *rsp_sz = 0;
+    num_entries = 0;
+    rc = 0;
 
     ble_hs_att_list_lock();
 
     STAILQ_FOREACH(ha, &g_ble_hs_att_list, ha_next) {
         if (ha->ha_handle_id > req->bhafq_end_handle) {
+            rc = 0;
             goto done;
         }
         if (ha->ha_handle_id >= req->bhafq_start_handle) {
             uuid16 = ble_hs_uuid_16bit(ha->ha_uuid);
 
             if (*format == 0) {
-                if (uuid16 != -1) {
+                if (uuid16 != 0) {
                     *format = BLE_HS_ATT_FIND_INFO_RSP_FORMAT_16BIT;
                 } else {
                     *format = BLE_HS_ATT_FIND_INFO_RSP_FORMAT_128BIT;
@@ -441,69 +446,97 @@ ble_hs_att_fill_info(struct ble_hs_att_find_info_req *req, uint8_t *buf,
 
             switch (*format) {
             case BLE_HS_ATT_FIND_INFO_RSP_FORMAT_16BIT:
-                if (uuid16 == -1) {
+                if (uuid16 == 0) {
+                    rc = 0;
+                    goto done;
+                }
+
+                rsp_sz = OS_MBUF_PKTHDR(om)->omp_len + 4;
+                if (rsp_sz > mtu) {
+                    rc = 0;
                     goto done;
                 }
 
-                new_rsp_sz = *rsp_sz + 4;
-                if (new_rsp_sz > buf_sz) {
+                htole16(&handle_id, ha->ha_handle_id);
+                rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &handle_id, 2);
+                if (rc != 0) {
                     goto done;
                 }
 
-                htole16(buf + *rsp_sz, ha->ha_handle_id);
-                htole16(buf + *rsp_sz + 2, uuid16);
-                *rsp_sz = new_rsp_sz;
+                htole16(&uuid16, uuid16);
+                rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &uuid16, 2);
+                if (rc != 0) {
+                    goto done;
+                }
                 break;
 
             case BLE_HS_ATT_FIND_INFO_RSP_FORMAT_128BIT:
-                if (uuid16 != -1) {
+                if (uuid16 != 0) {
+                    rc = 0;
+                    goto done;
+                }
+
+                rsp_sz = OS_MBUF_PKTHDR(om)->omp_len + 18;
+                if (rsp_sz > mtu) {
+                    rc = 0;
                     goto done;
                 }
 
-                new_rsp_sz = *rsp_sz + 18;
-                if (new_rsp_sz > buf_sz) {
+                htole16(&handle_id, ha->ha_handle_id);
+                rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &handle_id, 2);
+                if (rc != 0) {
                     goto done;
                 }
 
-                htole16(buf + *rsp_sz, ha->ha_handle_id);
-                memcpy(buf + *rsp_sz + 2, ha->ha_uuid, 16);
-                *rsp_sz = new_rsp_sz;
+                rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &ha->ha_uuid,
+                                    16);
+                if (rc != 0) {
+                    goto done;
+                }
                 break;
 
             default:
                 assert(0);
                 break;
             }
+
+            num_entries++;
         }
     }
 
 done:
     ble_hs_att_list_unlock();
-    if (*rsp_sz == 0) {
+
+    if (rc == 0 && num_entries == 0) {
         return ENOENT;
     } else {
-        return 0;
+        return rc;
     }
 }
 
 static int
 ble_hs_att_rx_find_info_req(struct ble_hs_conn *conn,
                             struct ble_l2cap_chan *chan,
-                            struct os_mbuf *om)
+                            struct os_mbuf *rxom)
 {
     struct ble_hs_att_find_info_req req;
     struct ble_hs_att_find_info_rsp rsp;
-    int rsp_sz;
+    struct os_mbuf *txom;
+    uint8_t buf[max(BLE_HS_ATT_FIND_INFO_REQ_SZ,
+                    BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ)];
     int rc;
 
-    rc = os_mbuf_copydata(om, 0, BLE_HS_ATT_FIND_INFO_REQ_SZ,
-                          ble_hs_att_tx_buf);
+    txom = NULL;
+
+    rc = os_mbuf_copydata(rxom, 0, BLE_HS_ATT_FIND_INFO_REQ_SZ, buf);
     if (rc != 0) {
-        return rc;
+        req.bhafq_start_handle = 0;
+        rc = BLE_HS_ATT_ERR_INVALID_PDU;
+        goto err;
     }
 
-    rc = ble_hs_att_find_info_req_parse(ble_hs_att_tx_buf,
-                                        BLE_HS_ATT_FIND_INFO_REQ_SZ, &req);
+    rc = ble_hs_att_find_info_req_parse(buf, BLE_HS_ATT_FIND_INFO_REQ_SZ,
+                                        &req);
     assert(rc == 0);
 
     /* Tx error response if start handle is greater than end handle or is equal
@@ -512,59 +545,358 @@ ble_hs_att_rx_find_info_req(struct ble_hs_conn *conn,
     if (req.bhafq_start_handle > req.bhafq_end_handle ||
         req.bhafq_start_handle == 0) {
 
-        ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_FIND_INFO_REQ,
-                                req.bhafq_start_handle,
-                                BLE_HS_ATT_ERR_INVALID_HANDLE);
-        return EINVAL;
+        rc = BLE_HS_ATT_ERR_INVALID_HANDLE;
+        goto err;
     }
 
-    /* Write the variable length Information Data field, reserving room for the
-     * response base at the start of the buffer.
-     */
-    rc = ble_hs_att_fill_info(
-        &req,
-        ble_hs_att_tx_buf + BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ,
-        ble_l2cap_chan_mtu(chan) - BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ,
-        &rsp_sz,
-        &rsp.bhafp_format);
-    if (rc != 0) {
-        ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_FIND_INFO_REQ,
-                                req.bhafq_start_handle,
-                                BLE_HS_ATT_ERR_ATTR_NOT_FOUND);
-        return rc;
-
+    txom = os_mbuf_get_pkthdr(&ble_l2cap_mbuf_pool);
+    if (txom == NULL) {
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
     }
 
-    /* Now that the value of the format field is known, write the response base
-     * at the start of the buffer.
+    /* Write the response base at the start of the buffer.  The format field is
+     * unknown at this point; it will be filled in later.
      */
     rsp.bhafp_op = BLE_HS_ATT_OP_FIND_INFO_RSP;
-    rc = ble_hs_att_find_info_rsp_write(ble_hs_att_tx_buf,
-                                        BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ,
+    rc = ble_hs_att_find_info_rsp_write(buf, BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ,
                                         &rsp);
     assert(rc == 0);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, txom, buf,
+                        BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ);
+    if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
+    /* Write the variable length Information Data field, populating the format
+     * field as appropriate.
+     */
+    rc = ble_hs_att_fill_info(&req, txom, ble_l2cap_chan_mtu(chan),
+                              txom->om_data + 1);
+    if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_ATTR_NOT_FOUND;
+        goto err;
+    }
+
+    rc = ble_l2cap_tx(chan, txom);
+    txom = NULL;
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    os_mbuf_free_chain(&ble_l2cap_mbuf_pool, txom);
+    ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_FIND_INFO_REQ,
+                            req.bhafq_start_handle, rc);
+
+    return rc;
+}
+
+/**
+ * Processes a single non-matching attribute entry while filling a
+ * Find-By-Type-Value-Response.
+ *
+ * @param om                    The response mbuf.
+ * @param first                 Pointer to the first matching handle ID in the
+ *                                  current group of IDs.  0 if there is not a
+ *                                  current group.
+ * @param prev                  Pointer to the most recent matching handle ID
+ *                                  in the current group of IDs.  0 if there is
+ *                                  not a current group.
+ * @param mtu                   The ATT L2CAP channel MTU.
+ *
+ * @return                      0 if the response should be sent;
+ *                              EAGAIN if the entry was successfully processed
+ *                                  and subsequent entries can be inspected.
+ *                              Other nonzero on error.
+ */
+static int
+ble_hs_att_fill_type_value_no_match(struct os_mbuf *om, uint16_t *first,
+                                    uint16_t *prev, int mtu)
+{
+    uint16_t u16;
+    int rsp_sz;
+    int rc;
+
+    /* If there is no current group, then there is nothing to do. */
+    if (*first == 0) {
+        return EAGAIN;
+    }
+
+    rsp_sz = OS_MBUF_PKTHDR(om)->omp_len + 4;
+    if (rsp_sz > mtu) {
+        return 0;
+    }
+
+    u16 = *first;
+    htole16(&u16, u16);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &u16, 2);
+    if (rc != 0) {
+        return ENOMEM;
+    }
 
-    rc = ble_l2cap_tx(chan, ble_hs_att_tx_buf,
-                      BLE_HS_ATT_FIND_INFO_RSP_MIN_SZ + rsp_sz);
+    u16 = *prev;
+    htole16(&u16, u16);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, &u16, 2);
     if (rc != 0) {
+        return ENOMEM;
+    }
+
+    *first = 0;
+    *prev = 0;
+
+    return EAGAIN;
+}
+
+/**
+ * Processes a single matching attribute entry while filling a
+ * Find-By-Type-Value-Response.
+ *
+ * @param om                    The response mbuf.
+ * @param first                 Pointer to the first matching handle ID in the
+ *                                  current group of IDs.  0 if there is not a
+ *                                  current group.
+ * @param prev                  Pointer to the most recent matching handle ID
+ *                                  in the current group of IDs.  0 if there is
+ *                                  not a current group.
+ * @param handle_id             The matching handle ID to process.
+ * @param mtu                   The ATT L2CAP channel MTU.
+ *
+ * @return                      0 if the response should be sent;
+ *                              EAGAIN if the entry was successfully processed
+ *                                  and subsequent entries can be inspected.
+ *                              Other nonzero on error.
+ */
+static int
+ble_hs_att_fill_type_value_match(struct os_mbuf *om, uint16_t *first,
+                                 uint16_t *prev, uint16_t handle_id, int mtu)
+{
+    int rc;
+
+    /* If this is the start of a group, record it as the first ID and keep
+     * searching.
+     */
+    if (*first == 0) {
+        *first = handle_id;
+        *prev = handle_id;
+        return EAGAIN;
+    }
+
+    /* If this is the continuation of a group, keep searching. */
+    if (handle_id == *prev + 1) {
+        *prev = handle_id;
+        return EAGAIN;
+    }
+
+    /* Otherwise, this handle is not a part of the previous group.  Write the
+     * previous group to the response, and remember this ID as the start of the
+     * next group.
+     */
+    rc = ble_hs_att_fill_type_value_no_match(om, first, prev, mtu);
+    *first = handle_id;
+    *prev = handle_id;
+    return rc;
+}
+
+/**
+ * Fills the supplied mbuf with the variable length Handles-Information-List
+ * field of a Find-By-Type-Value ATT response.
+ *
+ * @param req                   The Find-By-Type-Value-Request being responded
+ *                                  to.
+ * @param rxom                  The mbuf containing the received request.
+ * @param txom                  The destination mbuf where the
+ *                                  Handles-Information-List field gets
+ *                                  written.
+ * @param mtu                   The ATT L2CAP channel MTU.
+ *
+ * @return                      0 on success; an ATT error code on failure.
+ */
+static int
+ble_hs_att_fill_type_value(struct ble_hs_att_find_type_value_req *req,
+                           struct os_mbuf *rxom, struct os_mbuf *txom,
+                           uint16_t mtu)
+{
+    union ble_hs_att_handle_arg arg;
+    struct ble_hs_att_entry *ha;
+    uint16_t uuid16;
+    uint16_t first;
+    uint16_t prev;
+    int any_entries;
+    int match;
+    int rc;
+
+    first = 0;
+    prev = 0;
+    rc = 0;
+
+    ble_hs_att_list_lock();
+
+    /* Iterate through the attribute list, keeping track of the current
+     * matching group.  For each attribute entry, determine if data needs to be
+     * written to the response.
+     */
+    STAILQ_FOREACH(ha, &g_ble_hs_att_list, ha_next) {
+        match = 0;
+
+        if (ha->ha_handle_id > req->bhavq_end_handle) {
+            break;
+        }
+
+        if (ha->ha_handle_id >= req->bhavq_start_handle) {
+            /* Compare the attribute type and value to the request fields to
+             * determine if this attribute matches.
+             */
+            uuid16 = ble_hs_uuid_16bit(ha->ha_uuid);
+            if (uuid16 == req->bhavq_attr_type) {
+                rc = ha->ha_fn(ha, BLE_HS_ATT_OP_READ_REQ, &arg);
+                if (rc != 0) {
+                    rc = BLE_HS_ATT_ERR_UNLIKELY;
+                    goto done;
+                }
+                rc = os_mbuf_memcmp(rxom,
+                                    BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ,
+                                    arg.aha_read.attr_data,
+                                    arg.aha_read.attr_len);
+                if (rc == 0) {
+                    match = 1;
+                }
+            }
+        }
+
+        if (match) {
+            rc = ble_hs_att_fill_type_value_match(txom, &first, &prev,
+                                                  ha->ha_handle_id, mtu);
+        } else {
+            rc = ble_hs_att_fill_type_value_no_match(txom, &first, &prev, mtu);
+        }
+
+        if (rc == 0) {
+            goto done;
+        }
+        if (rc != EAGAIN) {
+            rc = BLE_HS_ATT_ERR_UNLIKELY;
+            goto done;
+        }
+    }
+
+    /* Process one last non-matching ID in case a group was in progress when
+     * the end of the attribute list was reached.
+     */
+    rc = ble_hs_att_fill_type_value_no_match(txom, &first, &prev, mtu);
+    if (rc == EAGAIN) {
+        rc = 0;
+    } else if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_UNLIKELY;
+    }
+
+done:
+    ble_hs_att_list_unlock();
+
+    any_entries = OS_MBUF_PKTHDR(txom)->omp_len >
+                  BLE_HS_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ;
+    if (rc == 0 && !any_entries) {
+        return BLE_HS_ATT_ERR_ATTR_NOT_FOUND;
+    } else {
         return rc;
     }
+}
+
+static int
+ble_hs_att_rx_find_type_value_req(struct ble_hs_conn *conn,
+                                  struct ble_l2cap_chan *chan,
+                                  struct os_mbuf *rxom)
+{
+    struct ble_hs_att_find_type_value_req req;
+    struct os_mbuf *txom;
+    uint8_t buf[max(BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ,
+                    BLE_HS_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ)];
+    int rc;
+
+    txom = NULL;
+
+    rc = os_mbuf_copydata(rxom, 0, BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ, buf);
+    if (rc != 0) {
+        req.bhavq_start_handle = 0;
+        rc = BLE_HS_ATT_ERR_INVALID_PDU;
+        goto err;
+    }
+
+    rc = ble_hs_att_find_type_value_req_parse(
+        buf, BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ, &req);
+    assert(rc == 0);
+
+    /* Tx error response if start handle is greater than end handle or is equal
+     * to 0 (Vol. 3, Part F, 3.4.3.3).
+     */
+    if (req.bhavq_start_handle > req.bhavq_end_handle ||
+        req.bhavq_start_handle == 0) {
+
+        rc = BLE_HS_ATT_ERR_INVALID_HANDLE;
+        goto err;
+    }
+
+    txom = os_mbuf_get_pkthdr(&ble_l2cap_mbuf_pool);
+    if (txom == NULL) {
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
+    /* Write the response base at the start of the buffer. */
+    buf[0] = BLE_HS_ATT_OP_FIND_TYPE_VALUE_RSP;
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, txom, buf,
+                        BLE_HS_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ);
+    if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
+    /* Write the variable length Information Data field. */
+    rc = ble_hs_att_fill_type_value(&req, rxom, txom,
+                                    ble_l2cap_chan_mtu(chan));
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_l2cap_tx(chan, txom);
+    txom = NULL;
+    if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_UNLIKELY;
+        goto err;
+    }
 
     return 0;
+
+err:
+    os_mbuf_free_chain(&ble_l2cap_mbuf_pool, txom);
+    ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ,
+                            req.bhavq_start_handle, rc);
+    return rc;
 }
 
 static int
 ble_hs_att_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                        void *attr_data, int attr_len)
 {
+    struct os_mbuf *txom;
     uint16_t data_len;
     uint8_t op;
     int rc;
 
+    txom = os_mbuf_get_pkthdr(&ble_l2cap_mbuf_pool);
+    if (txom == NULL) {
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
     op = BLE_HS_ATT_OP_READ_RSP;
-    rc = ble_l2cap_tx(chan, &op, 1);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, txom, &op, 1);
     if (rc != 0) {
-        return rc;
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
     }
 
     /* Vol. 3, part F, 3.2.9; don't send more than ATT_MTU-1 bytes of data. */
@@ -574,14 +906,23 @@ ble_hs_att_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         data_len = attr_len;
     }
 
-    rc = ble_l2cap_tx(chan, attr_data, data_len);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, txom, attr_data, data_len);
     if (rc != 0) {
-        return rc;
+        rc = BLE_HS_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
     }
 
-    /* XXX: Kick L2CAP. */
+    rc = ble_l2cap_tx(chan, txom);
+    if (rc != 0) {
+        rc = BLE_HS_ATT_ERR_UNLIKELY;
+        goto err;
+    }
 
     return 0;
+
+err:
+    os_mbuf_free_chain(&ble_l2cap_mbuf_pool, txom);
+    return rc;
 }
 
 static int
@@ -591,43 +932,45 @@ ble_hs_att_rx_read_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     union ble_hs_att_handle_arg arg;
     struct ble_hs_att_read_req req;
     struct ble_hs_att_entry *entry;
+    uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
     int rc;
 
-    rc = os_mbuf_copydata(om, 0, BLE_HS_ATT_READ_REQ_SZ, ble_hs_att_tx_buf);
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
     if (rc != 0) {
-        return rc;
+        req.bharq_handle = 0;
+        rc = BLE_HS_ATT_ERR_INVALID_PDU;
+        goto err;
     }
 
-    rc = ble_hs_att_read_req_parse(ble_hs_att_tx_buf, BLE_HS_ATT_READ_REQ_SZ,
-                                   &req);
+    rc = ble_hs_att_read_req_parse(buf, sizeof buf, &req);
     assert(rc == 0);
 
     rc = ble_hs_att_find_by_handle(req.bharq_handle, &entry);
     if (rc != 0) {
         rc = BLE_HS_ATT_ERR_INVALID_HANDLE;
-        goto send_err;
+        goto err;
     }
 
     if (entry->ha_fn == NULL) {
         rc = BLE_ERR_UNSPECIFIED;
-        goto send_err;
+        goto err;
     }
 
     rc = entry->ha_fn(entry, BLE_HS_ATT_OP_READ_REQ, &arg);
     if (rc != 0) {
         rc = BLE_ERR_UNSPECIFIED;
-        goto send_err;
+        goto err;
     }
 
     rc = ble_hs_att_tx_read_rsp(conn, chan, arg.aha_read.attr_data,
                                 arg.aha_read.attr_len);
     if (rc != 0) {
-        goto send_err;
+        goto err;
     }
 
     return 0;
 
-send_err:
+err:
     ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_READ_REQ,
                             req.bharq_handle, rc);
     return rc;
@@ -640,13 +983,11 @@ ble_hs_att_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
     int rc;
 
     op = BLE_HS_ATT_OP_WRITE_RSP;
-    rc = ble_l2cap_tx(chan, &op, 1);
+    rc = ble_l2cap_tx_flat(chan, &op, 1);
     if (rc != 0) {
         return rc;
     }
 
-    /* XXX: Kick L2CAP. */
-
     return 0;
 }
 
@@ -657,16 +998,15 @@ ble_hs_att_rx_write_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     union ble_hs_att_handle_arg arg;
     struct ble_hs_att_write_req req;
     struct ble_hs_att_entry *entry;
+    uint8_t buf[BLE_HS_ATT_WRITE_REQ_MIN_SZ];
     int rc;
 
-    rc = os_mbuf_copydata(om, 0, BLE_HS_ATT_WRITE_REQ_MIN_SZ,
-                          ble_hs_att_tx_buf);
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
     if (rc != 0) {
         return rc;
     }
 
-    rc = ble_hs_att_write_req_parse(ble_hs_att_tx_buf,
-                                    BLE_HS_ATT_WRITE_REQ_MIN_SZ, &req);
+    rc = ble_hs_att_write_req_parse(buf, sizeof buf, &req);
     assert(rc == 0);
 
     rc = ble_hs_att_find_by_handle(req.bhawq_handle, &entry);
@@ -772,21 +1112,11 @@ ble_hs_att_init(void)
         goto err;
     }
 
-    free(ble_hs_att_tx_buf);
-    ble_hs_att_tx_buf = malloc(BLE_HS_ATT_MTU_MAX);
-    if (ble_hs_att_tx_buf == NULL) {
-        rc = ENOMEM;
-        goto err;
-    }
-
     return 0;
 
 err:
     free(ble_hs_att_entry_mem);
     ble_hs_att_entry_mem = NULL;
 
-    free(ble_hs_att_tx_buf);
-    ble_hs_att_tx_buf = NULL;
-
     return rc;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/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 b8b5fe4..c782ea0 100644
--- a/net/nimble/host/src/ble_hs_att.h
+++ b/net/nimble/host/src/ble_hs_att.h
@@ -21,8 +21,10 @@
 #define BLE_HS_ATT_MTU_MAX          256 /* XXX: I'm making this up! */
 
 #define BLE_HS_ATT_ERR_INVALID_HANDLE       0x01
+#define BLE_HS_ATT_ERR_INVALID_PDU          0x04
 #define BLE_HS_ATT_ERR_REQ_NOT_SUPPORTED    0x06
 #define BLE_HS_ATT_ERR_ATTR_NOT_FOUND       0x0a
+#define BLE_HS_ATT_ERR_UNLIKELY             0x0e
 #define BLE_HS_ATT_ERR_INSUFFICIENT_RES     0x11
 
 struct ble_hs_att_entry;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/net/nimble/host/src/ble_hs_uuid.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_uuid.c b/net/nimble/host/src/ble_hs_uuid.c
index f905159..7a48569 100644
--- a/net/nimble/host/src/ble_hs_uuid.c
+++ b/net/nimble/host/src/ble_hs_uuid.c
@@ -29,9 +29,9 @@ static uint8_t ble_hs_uuid_base[16] = {
  *
  * @return                          Positive 16-bit unsigned integer on
  *                                      success;
- *                                  -1 if the UUID could not be converted.
+ *                                  0 if the UUID could not be converted.
  */
-int
+uint16_t
 ble_hs_uuid_16bit(uint8_t *uuid128)
 {
     uint16_t uuid16;
@@ -43,17 +43,17 @@ ble_hs_uuid_16bit(uint8_t *uuid128)
     rc = memcmp(uuid128 + 4, ble_hs_uuid_base + 4,
                 sizeof ble_hs_uuid_base - 4);
     if (rc != 0) {
-        return -1;
+        return 0;
     }
 
     if (uuid128[0] != 0 || uuid128[1] != 0) {
         /* This UUID has a 32-bit form, but not a 16-bit form. */
-        return -1;
+        return 0;
     }
 
     uuid16 = (uuid128[2] << 8) + uuid128[3];
     if (uuid16 == 0) {
-        return -1;
+        return 0;
     }
 
     return uuid16;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/net/nimble/host/src/ble_hs_uuid.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_uuid.h b/net/nimble/host/src/ble_hs_uuid.h
index 013b5cb..8604457 100644
--- a/net/nimble/host/src/ble_hs_uuid.h
+++ b/net/nimble/host/src/ble_hs_uuid.h
@@ -17,6 +17,6 @@
 #ifndef H_BLE_HS_UUID_
 #define H_BLE_HS_UUID_
 
-int ble_hs_uuid_16bit(uint8_t *uuid128);
+uint16_t ble_hs_uuid_16bit(uint8_t *uuid128);
 
 #endif /* _BLE_HOST_UUID_H */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/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 07d0bef..b7ac4c6 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -221,23 +221,65 @@ ble_l2cap_rx(struct ble_hs_conn *conn,
     return 0;
 }
 
+/**
+ * Transmits the L2CAP payload contained in the specified mbuf.  The supplied
+ * mbuf is consumed, regardless of the outcome of the function call.
+ *
+ * @param chan                  The L2CAP channel to transmit over.
+ * @param om                    The data to transmit.
+ *
+ * @return                      0 on success; nonzero on error.
+ */
 int
-ble_l2cap_tx(struct ble_l2cap_chan *chan, void *payload, int len)
+ble_l2cap_tx(struct ble_l2cap_chan *chan, struct os_mbuf *om)
 {
+    /* XXX Enqueue mbuf. */
+    if (chan->blc_tx_buf != NULL) {
+        os_mbuf_free_chain(&ble_l2cap_mbuf_pool, chan->blc_tx_buf);
+    }
+
+    chan->blc_tx_buf = om;
+
+    return 0;
+}
+
+/**
+ * Transmits the L2CAP payload contained in the specified flat buffer.
+ *
+ * @param chan                  The L2CAP channel to transmit over.
+ * @param payload               The data to transmit.
+ * @param len                   The number of data bytes to send.
+ *
+ * @return                      0 on success; nonzero on error.
+ */
+int
+ble_l2cap_tx_flat(struct ble_l2cap_chan *chan, void *payload, int len)
+{
+    struct os_mbuf *om;
     int rc;
 
-    rc = ble_l2cap_ensure_buf(&chan->blc_tx_buf);
+    om = os_mbuf_get_pkthdr(&ble_l2cap_mbuf_pool);
+    if (om == NULL) {
+        rc = ENOMEM;
+        goto done;
+    }
+
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, om, payload, len);
     if (rc != 0) {
-        /* XXX Need to deal with this in a way that prevents starvation. */
-        return ENOMEM;
+        goto done;
     }
 
-    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, payload, len);
+    rc = ble_l2cap_tx(chan, om);
+    om = NULL;
     if (rc != 0) {
-        return rc;
+        goto done;
     }
 
-    return 0;
+    rc = 0;
+
+done:
+    os_mbuf_free_chain(&ble_l2cap_mbuf_pool, om);
+    return rc;
 }
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/net/nimble/host/src/ble_l2cap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap.h b/net/nimble/host/src/ble_l2cap.h
index 0cad13f..b6b1c1c 100644
--- a/net/nimble/host/src/ble_l2cap.h
+++ b/net/nimble/host/src/ble_l2cap.h
@@ -79,7 +79,8 @@ int ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 int ble_l2cap_rx(struct ble_hs_conn *connection,
                  struct hci_data_hdr *hci_hdr,
                  void *pkt);
-int ble_l2cap_tx(struct ble_l2cap_chan *chan, void *payload, int len);
+int ble_l2cap_tx(struct ble_l2cap_chan *chan, struct os_mbuf *om);
+int ble_l2cap_tx_flat(struct ble_l2cap_chan *chan, void *payload, int len);
 
 int ble_l2cap_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/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 82fab92..ad05f8d 100644
--- a/net/nimble/host/src/test/ble_hs_att_test.c
+++ b/net/nimble/host/src/test/ble_hs_att_test.c
@@ -27,11 +27,13 @@
 #include "ble_hs_att.h"
 #include "ble_hs_att_cmd.h"
 
-static uint8_t *ble_hs_att_test_attr_1;
-static int ble_hs_att_test_attr_1_len;
+static uint8_t *ble_hs_att_test_attr_r_1;
+static int ble_hs_att_test_attr_r_1_len;
+static uint8_t *ble_hs_att_test_attr_r_2;
+static int ble_hs_att_test_attr_r_2_len;
 
-static uint8_t ble_hs_att_test_attr_2[1024];
-static int ble_hs_att_test_attr_2_len;
+static uint8_t ble_hs_att_test_attr_w_1[1024];
+static int ble_hs_att_test_attr_w_1_len;
 
 static void
 ble_hs_att_test_misc_init(struct ble_hs_conn **conn,
@@ -51,13 +53,13 @@ ble_hs_att_test_misc_init(struct ble_hs_conn **conn,
 }
 
 static int
-ble_hs_att_test_misc_attr_fn_1(struct ble_hs_att_entry *entry,
-                               uint8_t op, union ble_hs_att_handle_arg *arg)
+ble_hs_att_test_misc_attr_fn_r_1(struct ble_hs_att_entry *entry,
+                                 uint8_t op, union ble_hs_att_handle_arg *arg)
 {
     switch (op) {
     case BLE_HS_ATT_OP_READ_REQ:
-        arg->aha_read.attr_data = ble_hs_att_test_attr_1;
-        arg->aha_read.attr_len = ble_hs_att_test_attr_1_len;
+        arg->aha_read.attr_data = ble_hs_att_test_attr_r_1;
+        arg->aha_read.attr_len = ble_hs_att_test_attr_r_1_len;
         return 0;
 
     default:
@@ -66,8 +68,23 @@ ble_hs_att_test_misc_attr_fn_1(struct ble_hs_att_entry *entry,
 }
 
 static int
-ble_hs_att_test_misc_attr_fn_2(struct ble_hs_att_entry *entry,
-                               uint8_t op, union ble_hs_att_handle_arg *arg)
+ble_hs_att_test_misc_attr_fn_r_2(struct ble_hs_att_entry *entry,
+                                 uint8_t op, union ble_hs_att_handle_arg *arg)
+{
+    switch (op) {
+    case BLE_HS_ATT_OP_READ_REQ:
+        arg->aha_read.attr_data = ble_hs_att_test_attr_r_2;
+        arg->aha_read.attr_len = ble_hs_att_test_attr_r_2_len;
+        return 0;
+
+    default:
+        return -1;
+    }
+}
+
+static int
+ble_hs_att_test_misc_attr_fn_w_1(struct ble_hs_att_entry *entry,
+                                 uint8_t op, union ble_hs_att_handle_arg *arg)
 {
     struct os_mbuf_pkthdr *omp;
     int rc;
@@ -76,9 +93,9 @@ ble_hs_att_test_misc_attr_fn_2(struct ble_hs_att_entry *entry,
     case BLE_HS_ATT_OP_WRITE_REQ:
         omp = OS_MBUF_PKTHDR(arg->aha_write.om);
         rc = os_mbuf_copydata(arg->aha_write.om, 0, arg->aha_write.attr_len,
-                              ble_hs_att_test_attr_2);
+                              ble_hs_att_test_attr_w_1);
         TEST_ASSERT(rc == 0);
-        ble_hs_att_test_attr_2_len = arg->aha_write.attr_len;
+        ble_hs_att_test_attr_w_1_len = arg->aha_write.attr_len;
         return 0;
 
     default:
@@ -232,7 +249,55 @@ ble_hs_att_test_misc_verify_tx_find_info_rsp(
         }
     }
 
-    /* Remove the error response from the buffer. */
+    /* Ensure there is no extra data in the response. */
+    TEST_ASSERT(off == OS_MBUF_PKTHDR(chan->blc_tx_buf)->omp_len);
+
+    /* Remove the response from the buffer. */
+    os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, off);
+}
+
+struct ble_hs_att_test_type_value_entry {
+    uint16_t first;        /* 0 on last entry */
+    uint16_t last;
+};
+
+static void
+ble_hs_att_test_misc_verify_tx_find_type_value_rsp(
+    struct ble_l2cap_chan *chan,
+    struct ble_hs_att_test_type_value_entry *entries)
+{
+    struct ble_hs_att_test_type_value_entry *entry;
+    uint16_t u16;
+    uint8_t op;
+    int off;
+    int rc;
+
+    off = 0;
+
+    rc = os_mbuf_copydata(chan->blc_tx_buf, off, 1, &op);
+    TEST_ASSERT(rc == 0);
+    off += 1;
+
+    TEST_ASSERT(op == BLE_HS_ATT_OP_FIND_TYPE_VALUE_RSP);
+
+    for (entry = entries; entry->first != 0; entry++) {
+        rc = os_mbuf_copydata(chan->blc_tx_buf, off, 2, &u16);
+        TEST_ASSERT(rc == 0);
+        htole16(&u16, u16);
+        TEST_ASSERT(u16 == entry->first);
+        off += 2;
+
+        rc = os_mbuf_copydata(chan->blc_tx_buf, off, 2, &u16);
+        TEST_ASSERT(rc == 0);
+        htole16(&u16, u16);
+        TEST_ASSERT(u16 == entry->last);
+        off += 2;
+    }
+
+    /* Ensure there is no extra data in the response. */
+    TEST_ASSERT(off == OS_MBUF_PKTHDR(chan->blc_tx_buf)->omp_len);
+
+    /* Remove the response from the buffer. */
     os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, off);
 }
 
@@ -305,10 +370,10 @@ TEST_CASE(ble_hs_att_test_read)
                                            BLE_HS_ATT_ERR_INVALID_HANDLE);
 
     /*** Successful read. */
-    ble_hs_att_test_attr_1 = (uint8_t[]){0,1,2,3,4,5,6,7};
-    ble_hs_att_test_attr_1_len = 8;
+    ble_hs_att_test_attr_r_1 = (uint8_t[]){0,1,2,3,4,5,6,7};
+    ble_hs_att_test_attr_r_1_len = 8;
     rc = ble_hs_att_register(uuid, 0, &req.bharq_handle,
-                             ble_hs_att_test_misc_attr_fn_1);
+                             ble_hs_att_test_misc_attr_fn_r_1);
     TEST_ASSERT(rc == 0);
 
     rc = ble_hs_att_read_req_write(buf, sizeof buf, &req);
@@ -317,15 +382,15 @@ TEST_CASE(ble_hs_att_test_read)
     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,
-                                            ble_hs_att_test_attr_1_len);
+    ble_hs_att_test_misc_verify_tx_read_rsp(chan, ble_hs_att_test_attr_r_1,
+                                            ble_hs_att_test_attr_r_1_len);
 
 
     /*** Partial read. */
-    ble_hs_att_test_attr_1 =
+    ble_hs_att_test_attr_r_1 =
         (uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,
                     22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39};
-    ble_hs_att_test_attr_1_len = 40;
+    ble_hs_att_test_attr_r_1_len = 40;
 
     rc = ble_hs_att_read_req_write(buf, sizeof buf, &req);
     TEST_ASSERT(rc == 0);
@@ -333,7 +398,7 @@ TEST_CASE(ble_hs_att_test_read)
     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,
+    ble_hs_att_test_misc_verify_tx_read_rsp(chan, ble_hs_att_test_attr_r_1,
                                             BLE_HS_ATT_MTU_DFLT - 1);
 }
 
@@ -362,7 +427,7 @@ TEST_CASE(ble_hs_att_test_write)
 
     /*** Successful write. */
     rc = ble_hs_att_register(uuid, 0, &req.bhawq_handle,
-                             ble_hs_att_test_misc_attr_fn_2);
+                             ble_hs_att_test_misc_attr_fn_w_1);
     TEST_ASSERT(rc == 0);
 
     rc = ble_hs_att_write_req_write(buf, sizeof buf, &req);
@@ -442,7 +507,7 @@ TEST_CASE(ble_hs_att_test_find_info)
 
     /*** Range too late. */
     rc = ble_hs_att_register(uuid1, 0, &handle1,
-                             ble_hs_att_test_misc_attr_fn_1);
+                             ble_hs_att_test_misc_attr_fn_r_1);
     TEST_ASSERT(rc == 0);
 
     req.bhafq_start_handle = 200;
@@ -477,7 +542,7 @@ TEST_CASE(ble_hs_att_test_find_info)
 
     /*** Two 128-bit entries. */
     rc = ble_hs_att_register(uuid2, 0,
-                             &handle2, ble_hs_att_test_misc_attr_fn_1);
+                             &handle2, ble_hs_att_test_misc_attr_fn_r_1);
     TEST_ASSERT(rc == 0);
 
     req.bhafq_start_handle = handle1;
@@ -502,7 +567,7 @@ TEST_CASE(ble_hs_att_test_find_info)
 
     /*** Two 128-bit entries; 16-bit entry doesn't get sent. */
     rc = ble_hs_att_register(uuid3, 0,
-                             &handle3, ble_hs_att_test_misc_attr_fn_1);
+                             &handle3, ble_hs_att_test_misc_attr_fn_r_1);
     TEST_ASSERT(rc == 0);
 
     req.bhafq_start_handle = handle1;
@@ -544,12 +609,228 @@ TEST_CASE(ble_hs_att_test_find_info)
         } }));
 }
 
+TEST_CASE(ble_hs_att_test_find_type_value)
+{
+    struct ble_hs_att_find_type_value_req req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    uint8_t buf[BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ + 2];
+    uint16_t handle1;
+    uint16_t handle2;
+    uint16_t handle3;
+    uint16_t handle4;
+    uint16_t handle5;
+    uint8_t uuid1[16] = {
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
+        0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+    };
+    uint8_t uuid2[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
+    uint8_t uuid3[16] = {
+        0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
+        0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+    };
+    int rc;
+
+    ble_hs_att_test_misc_init(&conn, &chan);
+
+    /* Increase the MTU to 128 bytes to allow testing of long responses. */
+    chan->blc_my_mtu = 128;
+    chan->blc_peer_mtu = 128;
+    chan->blc_flags |= BLE_L2CAP_CHAN_F_TXED_MTU;
+
+    /* One-time write of the attribute value at the end of the request. */
+    ble_hs_att_test_attr_r_1 = (uint8_t[]){0x99, 0x99};
+    ble_hs_att_test_attr_r_1_len = 2;
+    memcpy(buf + BLE_HS_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ, ble_hs_att_test_attr_r_1,
+           ble_hs_att_test_attr_r_1_len);
+
+    /*** Start handle of 0. */
+    req.bhavq_op = BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ;
+    req.bhavq_start_handle = 0;
+    req.bhavq_end_handle = 0;
+    req.bhavq_attr_type = 0x0001;
+
+    rc = ble_hs_att_find_type_value_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_err_rsp(
+        chan, BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ, 0,
+        BLE_HS_ATT_ERR_INVALID_HANDLE);
+
+    /*** Start handle > end handle. */
+    req.bhavq_start_handle = 101;
+    req.bhavq_end_handle = 100;
+
+    rc = ble_hs_att_find_type_value_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_err_rsp(
+        chan, BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ, 101,
+        BLE_HS_ATT_ERR_INVALID_HANDLE);
+
+    /*** No attributes. */
+    req.bhavq_start_handle = 200;
+    req.bhavq_end_handle = 300;
+
+    rc = ble_hs_att_find_type_value_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_err_rsp(
+        chan, BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ, 200,
+        BLE_HS_ATT_ERR_ATTR_NOT_FOUND);
+
+    /*** Range too late. */
+    rc = ble_hs_att_register(uuid1, 0, &handle1,
+                             ble_hs_att_test_misc_attr_fn_r_1);
+    TEST_ASSERT(rc == 0);
+
+    req.bhavq_start_handle = 200;
+    req.bhavq_end_handle = 300;
+
+    rc = ble_hs_att_find_type_value_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_err_rsp(
+        chan, BLE_HS_ATT_OP_FIND_TYPE_VALUE_REQ, 200,
+        BLE_HS_ATT_ERR_ATTR_NOT_FOUND);
+
+    /*** One entry, one attribute. */
+    req.bhavq_start_handle = handle1;
+    req.bhavq_end_handle = handle1;
+
+    rc = ble_hs_att_find_type_value_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_find_type_value_rsp(chan,
+        ((struct ble_hs_att_test_type_value_entry[]) { {
+            .first = handle1,
+            .last = handle1,
+        }, {
+            .first = 0,
+        } }));
+
+    /*** One entry, two attributes. */
+    rc = ble_hs_att_register(uuid1, 0,
+                             &handle2, ble_hs_att_test_misc_attr_fn_r_1);
+    TEST_ASSERT(rc == 0);
+
+    req.bhavq_start_handle = handle1;
+    req.bhavq_end_handle = handle2;
+
+    rc = ble_hs_att_find_type_value_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_find_type_value_rsp(chan,
+        ((struct ble_hs_att_test_type_value_entry[]) { {
+            .first = handle1,
+            .last = handle2,
+        }, {
+            .first = 0,
+        } }));
+
+    /*** Entry 1: two attributes; entry 2: one attribute. */
+    rc = ble_hs_att_register(uuid2, 0, &handle3,
+                             ble_hs_att_test_misc_attr_fn_r_2);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_hs_att_register(uuid1, 0, &handle4,
+                             ble_hs_att_test_misc_attr_fn_r_1);
+    TEST_ASSERT(rc == 0);
+
+    req.bhavq_start_handle = 0x0001;
+    req.bhavq_end_handle = 0xffff;
+
+    rc = ble_hs_att_find_type_value_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_find_type_value_rsp(chan,
+        ((struct ble_hs_att_test_type_value_entry[]) { {
+            .first = handle1,
+            .last = handle2,
+        }, {
+            .first = handle4,
+            .last = handle4,
+        }, {
+            .first = 0,
+        } }));
+
+    /*** Ensure attribute with wrong value is not included. */
+    ble_hs_att_test_attr_r_2 = (uint8_t[]){0x00, 0x00};
+    ble_hs_att_test_attr_r_2_len = 2;
+
+    req.bhavq_start_handle = 0x0001;
+    req.bhavq_end_handle = 0xffff;
+
+    rc = ble_hs_att_find_type_value_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_find_type_value_rsp(chan,
+        ((struct ble_hs_att_test_type_value_entry[]) { {
+            .first = handle1,
+            .last = handle2,
+        }, {
+            .first = handle4,
+            .last = handle4,
+        }, {
+            .first = 0,
+        } }));
+
+    /*** Ensure attribute with wrong type is not included. */
+    rc = ble_hs_att_register(uuid3, 0, &handle5,
+                             ble_hs_att_test_misc_attr_fn_r_1);
+
+    req.bhavq_start_handle = 0x0001;
+    req.bhavq_end_handle = 0xffff;
+
+    rc = ble_hs_att_find_type_value_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_find_type_value_rsp(chan,
+        ((struct ble_hs_att_test_type_value_entry[]) { {
+            .first = handle1,
+            .last = handle2,
+        }, {
+            .first = handle4,
+            .last = handle4,
+        }, {
+            .first = 0,
+        } }));
+}
+
 TEST_SUITE(att_suite)
 {
     ble_hs_att_test_mtu();
     ble_hs_att_test_read();
     ble_hs_att_test_write();
     ble_hs_att_test_find_info();
+    ble_hs_att_test_find_type_value();
 }
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34a6d69f/net/nimble/host/src/test/ble_hs_uuid_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_uuid_test.c b/net/nimble/host/src/test/ble_hs_uuid_test.c
index fa1dfcd..698aa32 100644
--- a/net/nimble/host/src/test/ble_hs_uuid_test.c
+++ b/net/nimble/host/src/test/ble_hs_uuid_test.c
@@ -22,7 +22,7 @@
 
 TEST_CASE(ble_hs_uuid_test_128_to_16)
 {
-    int uuid16;
+    uint16_t uuid16;
 
     /*** RFCOMM */
     uuid16 = ble_hs_uuid_16bit(((uint8_t[]) {
@@ -52,19 +52,19 @@ TEST_CASE(ble_hs_uuid_test_128_to_16)
     uuid16 = ble_hs_uuid_16bit(((uint8_t[]) {
         0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00,
         0x80, 0x00, 0x00, 0x80, 0x5f, 0x9c, 0x34, 0xfb}));
-    TEST_ASSERT(uuid16 == -1);
+    TEST_ASSERT(uuid16 == 0);
 
     /*** Invalid prefix. */
     uuid16 = ble_hs_uuid_16bit(((uint8_t[]) {
         0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00,
         0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}));
-    TEST_ASSERT(uuid16 == -1);
+    TEST_ASSERT(uuid16 == 0);
 
     /*** 16-bit UUID of 0. */
     uuid16 = ble_hs_uuid_16bit(((uint8_t[]) {
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
         0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}));
-    TEST_ASSERT(uuid16 == -1);
+    TEST_ASSERT(uuid16 == 0);
 }
 
 TEST_SUITE(ble_hs_uuid_test_suite)