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/12/04 23:41:50 UTC

incubator-mynewt-larva git commit: GATT Discover Primary Service by Service UUID proc

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 21a7e74ab -> 941319ca0


GATT Discover Primary Service by Service UUID proc


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

Branch: refs/heads/master
Commit: 941319ca0a1a360680be8d3c96683f9e296bc7da
Parents: 21a7e74
Author: Christopher Collins <cc...@gmail.com>
Authored: Fri Dec 4 14:41:29 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Fri Dec 4 14:41:29 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gatt.h        |   8 +-
 net/nimble/host/src/ble_att.c                  |   2 +
 net/nimble/host/src/ble_att.h                  |   9 +
 net/nimble/host/src/ble_att_clt.c              | 135 +++++++++++-
 net/nimble/host/src/ble_att_cmd.c              |   4 +-
 net/nimble/host/src/ble_att_cmd.h              |   5 +-
 net/nimble/host/src/ble_att_svr.c              |  10 +-
 net/nimble/host/src/ble_gatt.c                 | 218 ++++++++++++++++++--
 net/nimble/host/src/test/ble_gatt_test.c       | 207 ++++++++++++++++++-
 net/nimble/host/src/test/ble_hs_att_svr_test.c |   4 +-
 10 files changed, 552 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/include/host/ble_gatt.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_gatt.h b/net/nimble/host/include/host/ble_gatt.h
index 7aa3c98..b76389e 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -19,9 +19,10 @@ typedef int ble_gatt_disc_service_fn(uint16_t conn_handle, int status,
 int ble_gatt_disc_all_services(uint16_t conn_handle,
                                ble_gatt_disc_service_fn *cb,
                                void *cb_arg);
+int ble_gatt_disc_service_by_uuid(uint16_t conn_handle, void *service_uuid128,
+                                  ble_gatt_disc_service_fn *cb, void *cb_arg);
 
-void ble_gatt_rx_error(struct ble_hs_conn *conn,
-                       struct ble_att_error_rsp *rsp);
+void ble_gatt_rx_err(struct ble_hs_conn *conn, struct ble_att_error_rsp *rsp);
 void ble_gatt_wakeup(void);
 void ble_gatt_rx_mtu(struct ble_hs_conn *conn, uint16_t chan_mtu);
 int ble_gatt_mtu(uint16_t conn_handle);
@@ -30,6 +31,9 @@ void ble_gatt_rx_find_info(struct ble_hs_conn *conn, int status,
 void ble_gatt_rx_read_group_type_adata(struct ble_hs_conn *conn,
                                        struct ble_att_clt_adata *adata);
 void ble_gatt_rx_read_group_type_complete(struct ble_hs_conn *conn, int rc);
+void ble_gatt_rx_find_type_value_hinfo(struct ble_hs_conn *conn,
+                                       struct ble_att_clt_adata *adata);
+void ble_gatt_rx_find_type_value_complete(struct ble_hs_conn *conn, int rc);
 int ble_gatt_find_info(uint16_t conn_handle_id, uint16_t att_start_handle,
                        uint16_t att_end_handle);
 int ble_gatt_init(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/ble_att.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att.c b/net/nimble/host/src/ble_att.c
index 71abd6f..afef850 100644
--- a/net/nimble/host/src/ble_att.c
+++ b/net/nimble/host/src/ble_att.c
@@ -30,11 +30,13 @@ struct ble_att_rx_dispatch_entry {
 };
 
 static struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = {
+    { BLE_ATT_OP_ERROR_RSP,            ble_att_clt_rx_error },
     { BLE_ATT_OP_MTU_REQ,              ble_att_svr_rx_mtu },
     { BLE_ATT_OP_MTU_RSP,              ble_att_clt_rx_mtu },
     { BLE_ATT_OP_FIND_INFO_REQ,        ble_att_svr_rx_find_info },
     { BLE_ATT_OP_FIND_INFO_RSP,        ble_att_clt_rx_find_info },
     { BLE_ATT_OP_FIND_TYPE_VALUE_REQ,  ble_att_svr_rx_find_type_value },
+    { BLE_ATT_OP_FIND_TYPE_VALUE_RSP,  ble_att_clt_rx_find_type_value },
     { BLE_ATT_OP_READ_TYPE_REQ,        ble_att_svr_rx_read_type },
     { BLE_ATT_OP_READ_REQ,             ble_att_svr_rx_read },
     { BLE_ATT_OP_READ_GROUP_TYPE_RSP,  ble_att_clt_rx_read_group_type_rsp },

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/ble_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att.h b/net/nimble/host/src/ble_att.h
index b8e6439..93c74c2 100644
--- a/net/nimble/host/src/ble_att.h
+++ b/net/nimble/host/src/ble_att.h
@@ -28,6 +28,7 @@ struct ble_att_mtu_cmd;
 struct ble_att_read_req;
 struct ble_att_read_group_type_req;
 struct ble_att_read_group_type_rsp;
+struct ble_att_find_type_value_req;
 
 #define BLE_ATT_MTU_DFLT         23  /* Also the minimum. */
 #define BLE_ATT_MTU_MAX          256 /* XXX: I'm making this up! */
@@ -139,6 +140,8 @@ int ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 int ble_att_svr_init(void);
 
 /*** @clt */
+int ble_att_clt_rx_error(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                         struct os_mbuf **om);
 int ble_att_clt_tx_mtu(struct ble_hs_conn *conn,
                        struct ble_att_mtu_cmd *req);
 int ble_att_clt_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
@@ -149,6 +152,12 @@ int ble_att_clt_tx_find_info(struct ble_hs_conn *conn,
                              struct ble_att_find_info_req *req);
 int ble_att_clt_rx_find_info(struct ble_hs_conn *conn,
                              struct ble_l2cap_chan *chan, struct os_mbuf **om);
+int ble_att_clt_tx_find_type_value(struct ble_hs_conn *conn,
+                                   struct ble_att_find_type_value_req *req,
+                                   void *attribute_value, int value_len);
+int ble_att_clt_rx_find_type_value(struct ble_hs_conn *conn,
+                                   struct ble_l2cap_chan *chan,
+                                   struct os_mbuf **rxom);
 int ble_att_clt_tx_read_group_type(struct ble_hs_conn *conn,
                                    struct ble_att_read_group_type_req *req,
                                    void *uuid128);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/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 05684b6..20b584d 100644
--- a/net/nimble/host/src/ble_att_clt.c
+++ b/net/nimble/host/src/ble_att_clt.c
@@ -61,6 +61,28 @@ err:
 }
 
 int
+ble_att_clt_rx_error(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                     struct os_mbuf **om)
+{
+    struct ble_att_error_rsp rsp;
+    int rc;
+
+    *om = os_mbuf_pullup(*om, BLE_ATT_ERROR_RSP_SZ);
+    if (*om == NULL) {
+        return ENOMEM;
+    }
+
+    rc = ble_att_error_rsp_parse((*om)->om_data, (*om)->om_len, &rsp);
+    if (rc != 0) {
+        return rc;
+    }
+
+    ble_gatt_rx_err(conn, &rsp);
+
+    return 0;
+}
+
+int
 ble_att_clt_tx_mtu(struct ble_hs_conn *conn, struct ble_att_mtu_cmd *req)
 {
     struct ble_l2cap_chan *chan;
@@ -273,6 +295,104 @@ err:
 }
 
 int
+ble_att_clt_tx_find_type_value(struct ble_hs_conn *conn,
+                               struct ble_att_find_type_value_req *req,
+                               void *attribute_value, int value_len)
+{
+    struct ble_l2cap_chan *chan;
+    struct os_mbuf *txom;
+    int rc;
+
+    txom = NULL;
+
+    if (req->bhavq_start_handle == 0 ||
+        req->bhavq_start_handle > req->bhavq_end_handle) {
+
+        rc = EINVAL;
+        goto err;
+    }
+
+    rc = ble_att_clt_prep_req(conn, &chan, &txom,
+                              BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ);
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_att_find_type_value_req_write(txom->om_data, txom->om_len, req);
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = os_mbuf_append(txom, attribute_value, value_len);
+    if (rc != 0) {
+        rc = EMSGSIZE;
+        goto err;
+    }
+
+    rc = ble_l2cap_tx(chan, txom);
+    txom = NULL;
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+static int
+ble_att_clt_parse_handles_info(struct os_mbuf **om,
+                               struct ble_att_clt_adata *adata)
+{
+    *om = os_mbuf_pullup(*om, BLE_ATT_FIND_TYPE_VALUE_HINFO_BASE_SZ);
+    if (*om == NULL) {
+        return ENOMEM;
+    }
+
+    adata->att_handle = le16toh((*om)->om_data + 0);
+    adata->end_group_handle = le16toh((*om)->om_data + 2);
+    adata->value_len = 0;
+    adata->value = NULL;
+
+    return 0;
+}
+
+int
+ble_att_clt_rx_find_type_value(struct ble_hs_conn *conn,
+                               struct ble_l2cap_chan *chan,
+                               struct os_mbuf **rxom)
+{
+    struct ble_att_clt_adata adata;
+    int rc;
+
+    /* Reponse consists of a one-byte opcode (already verified) and a variable
+     * length Handles Information List field.  Strip the opcode from the
+     * response.
+     */
+    os_mbuf_adj(*rxom, BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ);
+
+    /* Parse the Handles Information List field, passing each entry to the
+     * GATT.
+     */
+    while (OS_MBUF_PKTLEN(*rxom) > 0) {
+        rc = ble_att_clt_parse_handles_info(rxom, &adata);
+        if (rc != 0) {
+            break;
+        }
+
+        ble_gatt_rx_find_type_value_hinfo(conn, &adata);
+        os_mbuf_adj(*rxom, BLE_ATT_FIND_TYPE_VALUE_HINFO_BASE_SZ);
+    }
+
+    /* Notify GATT that the full response has been parsed. */
+    ble_gatt_rx_find_type_value_complete(conn, rc);
+
+    return 0;
+}
+
+int
 ble_att_clt_tx_read_group_type(struct ble_hs_conn *conn,
                                struct ble_att_read_group_type_req *req,
                                void *uuid128)
@@ -348,31 +468,32 @@ ble_att_clt_rx_read_group_type_rsp(struct ble_hs_conn *conn,
 
     *rxom = os_mbuf_pullup(*rxom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
     if (*rxom == NULL) {
-        return ENOMEM;
+        rc = ENOMEM;
+        goto done;
     }
 
     rc = ble_att_read_group_type_rsp_parse((*rxom)->om_data, (*rxom)->om_len,
                                            &rsp);
     if (rc != 0) {
-        return rc;
+        goto done;
     }
 
+    /* Strip the base from the front of the response. */
     os_mbuf_adj(*rxom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
 
-    /* XXX: Verify group handle is valid. */
-
+    /* Parse the Attribute Data List field, passing each entry to the GATT. */
     while (OS_MBUF_PKTLEN(*rxom) > 0) {
         rc = ble_att_clt_parse_attribute_data(rxom, rsp.bhagp_length, &adata);
         if (rc != 0) {
-            break;
+            goto done;
         }
 
-        /* Pass attribute data to GATT callback. */
         ble_gatt_rx_read_group_type_adata(conn, &adata);
-
         os_mbuf_adj(*rxom, rsp.bhagp_length);
     }
 
+done:
+    /* Notify GATT that the response is done being parsed. */
     ble_gatt_rx_read_group_type_complete(conn, rc);
 
     return 0;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/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 87ade7b..09718d8 100644
--- a/net/nimble/host/src/ble_att_cmd.c
+++ b/net/nimble/host/src/ble_att_cmd.c
@@ -206,7 +206,7 @@ ble_att_find_type_value_req_parse(void *payload, int len,
 {
     uint8_t *u8ptr;
 
-    if (len < BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ) {
+    if (len < BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ) {
         return EMSGSIZE;
     }
 
@@ -229,7 +229,7 @@ ble_att_find_type_value_req_write(void *payload, int len,
 {
     uint8_t *u8ptr;
 
-    if (len < BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ) {
+    if (len < BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ) {
         return EMSGSIZE;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/ble_att_cmd.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_cmd.h b/net/nimble/host/src/ble_att_cmd.h
index 443e684..3b4cdf6 100644
--- a/net/nimble/host/src/ble_att_cmd.h
+++ b/net/nimble/host/src/ble_att_cmd.h
@@ -97,7 +97,7 @@ struct ble_att_find_info_rsp {
  * | Attribute Type                     | 2                 |
  * | Attribute Value                    | 0 to (ATT_MTU-7)  |
  */
-#define BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ   7
+#define BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ   7
 struct ble_att_find_type_value_req {
     uint16_t bhavq_start_handle;
     uint16_t bhavq_end_handle;
@@ -110,7 +110,8 @@ struct ble_att_find_type_value_req {
  * | Attribute Opcode                   | 1                 |
  * | Information Data                   | 4 to (ATT_MTU-1)  |
  */
-#define BLE_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ   1
+#define BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ     1
+#define BLE_ATT_FIND_TYPE_VALUE_HINFO_BASE_SZ   4
 
 /**
  * | Parameter                          | Size (octets)     |

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/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 90d3e3b..25f9cb8 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -734,7 +734,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
                     goto done;
                 }
                 rc = os_mbuf_memcmp(rxom,
-                                    BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ,
+                                    BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ,
                                     arg.aha_read.attr_data,
                                     arg.aha_read.attr_len);
                 if (rc == 0) {
@@ -745,10 +745,10 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
 
         if (match) {
             rc = ble_att_svr_fill_type_value_match(txom, &first, &prev,
-                                                      ha->ha_handle_id, mtu);
+                                                   ha->ha_handle_id, mtu);
         } else {
             rc = ble_att_svr_fill_type_value_no_match(txom, &first, &prev,
-                                                         mtu);
+                                                      mtu);
         }
 
         if (rc == 0) {
@@ -774,7 +774,7 @@ done:
     ble_att_svr_list_unlock();
 
     any_entries = OS_MBUF_PKTHDR(txom)->omp_len >
-                  BLE_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ;
+                  BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ;
     if (rc == 0 && !any_entries) {
         return BLE_ATT_ERR_ATTR_NOT_FOUND;
     } else {
@@ -820,7 +820,7 @@ ble_att_svr_rx_find_type_value(struct ble_hs_conn *conn,
     }
 
     /* Write the response base at the start of the buffer. */
-    buf = os_mbuf_extend(txom, BLE_ATT_FIND_TYPE_VALUE_RSP_MIN_SZ);
+    buf = os_mbuf_extend(txom, BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ);
     if (buf == NULL) {
         rc = BLE_ATT_ERR_INSUFFICIENT_RES;
         goto err;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/ble_gatt.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt.c b/net/nimble/host/src/ble_gatt.c
index f472730..e357240 100644
--- a/net/nimble/host/src/ble_gatt.c
+++ b/net/nimble/host/src/ble_gatt.c
@@ -55,6 +55,13 @@ struct ble_gatt_entry {
             ble_gatt_disc_service_fn *cb;
             void *cb_arg;
         } disc_all_services;
+
+        struct {
+            uint8_t service_uuid[16];
+            uint16_t prev_handle;
+            ble_gatt_disc_service_fn *cb;
+            void *cb_arg;
+        } disc_service_uuid;
     };
 };
 
@@ -62,18 +69,50 @@ struct ble_gatt_entry {
 #define BLE_GATT_OP_MTU                         0
 #define BLE_GATT_OP_FIND_INFO                   1
 #define BLE_GATT_OP_DISC_ALL_SERVICES           2
-#define BLE_GATT_OP_MAX                         3
+#define BLE_GATT_OP_DISC_SERVICE_UUID           3
+#define BLE_GATT_OP_MAX                         4
 
 typedef int ble_gatt_kick_fn(struct ble_gatt_entry *entry);
+typedef int ble_gatt_rx_err_fn(struct ble_gatt_entry *entry,
+                               struct ble_hs_conn *conn,
+                               struct ble_att_error_rsp *rsp);
 
 static int ble_gatt_kick_mtu(struct ble_gatt_entry *entry);
 static int ble_gatt_kick_find_info(struct ble_gatt_entry *entry);
 static int ble_gatt_kick_disc_all_services(struct ble_gatt_entry *entry);
+static int ble_gatt_kick_disc_service_uuid(struct ble_gatt_entry *entry);
+
+static int ble_gatt_rx_err_disc_all_services(struct ble_gatt_entry *entry,
+                                             struct ble_hs_conn *conn,
+                                             struct ble_att_error_rsp *rsp);
+static int ble_gatt_rx_err_disc_service_uuid(struct ble_gatt_entry *entry,
+                                             struct ble_hs_conn *conn,
+                                             struct ble_att_error_rsp *rsp);
+
+struct ble_gatt_dispatch_entry {
+    ble_gatt_kick_fn *kick_cb;
+    ble_gatt_rx_err_fn *rx_err_cb;
+};
 
-static ble_gatt_kick_fn *ble_gatt_kick_fns[BLE_GATT_OP_MAX] = {
-    [BLE_GATT_OP_MTU] =                 ble_gatt_kick_mtu,
-    [BLE_GATT_OP_FIND_INFO] =           ble_gatt_kick_find_info,
-    [BLE_GATT_OP_DISC_ALL_SERVICES] =   ble_gatt_kick_disc_all_services,
+static const struct ble_gatt_dispatch_entry
+    ble_gatt_dispatch[BLE_GATT_OP_MAX] = {
+
+    [BLE_GATT_OP_MTU] = {
+        .kick_cb = ble_gatt_kick_mtu,
+        .rx_err_cb = NULL,
+    },
+    [BLE_GATT_OP_FIND_INFO] = {
+        .kick_cb = ble_gatt_kick_find_info,
+        .rx_err_cb = NULL,
+    },
+    [BLE_GATT_OP_DISC_ALL_SERVICES] = {
+        .kick_cb = ble_gatt_kick_disc_all_services,
+        .rx_err_cb = ble_gatt_rx_err_disc_all_services,
+    },
+    [BLE_GATT_OP_DISC_SERVICE_UUID] = {
+        .kick_cb = ble_gatt_kick_disc_service_uuid,
+        .rx_err_cb = ble_gatt_rx_err_disc_service_uuid,
+    },
 };
 
 #define BLE_GATT_ENTRY_F_PENDING    0x01
@@ -85,6 +124,13 @@ static struct os_mempool ble_gatt_entry_pool;
 
 static STAILQ_HEAD(, ble_gatt_entry) ble_gatt_list;
 
+static const struct ble_gatt_dispatch_entry *
+ble_gatt_dispatch_get(uint8_t op)
+{
+    assert(op < BLE_GATT_OP_MAX);
+    return ble_gatt_dispatch + op;
+}
+
 static struct ble_gatt_entry *
 ble_gatt_entry_alloc(void)
 {
@@ -294,9 +340,36 @@ ble_gatt_kick_disc_all_services(struct ble_gatt_entry *entry)
     return 0;
 }
 
+static int
+ble_gatt_kick_disc_service_uuid(struct ble_gatt_entry *entry)
+{
+    struct ble_att_find_type_value_req req;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        return ENOTCONN;
+    }
+
+    req.bhavq_start_handle = entry->disc_service_uuid.prev_handle + 1;
+    req.bhavq_end_handle = 0xffff;
+    req.bhavq_attr_type = BLE_ATT_UUID_PRIMARY_SERVICE;
+
+    rc = ble_att_clt_tx_find_type_value(conn, &req,
+                                        entry->disc_service_uuid.service_uuid,
+                                        16);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
 void
 ble_gatt_wakeup(void)
 {
+    const struct ble_gatt_dispatch_entry *dispatch;
     struct ble_gatt_entry *entry;
     struct ble_gatt_entry *prev;
     struct ble_gatt_entry *next;
@@ -311,9 +384,8 @@ ble_gatt_wakeup(void)
         next = STAILQ_NEXT(entry, next);
 
         if (entry->flags & BLE_GATT_ENTRY_F_PENDING) {
-            assert(entry->op < BLE_GATT_OP_MAX);
-
-            rc = ble_gatt_kick_fns[entry->op](entry);
+            dispatch = ble_gatt_dispatch_get(entry->op);
+            rc = dispatch->kick_cb(entry);
             if (rc == 0) {
                 ble_gatt_entry_set_expecting(entry, prev);
             } else {
@@ -326,9 +398,52 @@ ble_gatt_wakeup(void)
     }
 }
 
+static int
+ble_gatt_rx_err_disc_all_services(struct ble_gatt_entry *entry,
+                                  struct ble_hs_conn *conn,
+                                  struct ble_att_error_rsp *rsp)
+{
+    uint8_t status;
+
+    if (rsp->bhaep_error_code == BLE_ATT_ERR_ATTR_NOT_FOUND) {
+        /* Discovery is complete. */
+        status = 0;
+    } else {
+        /* Discovery failure. */
+        status = rsp->bhaep_error_code;
+    }
+
+    entry->disc_all_services.cb(conn->bhc_handle, status, NULL,
+                                entry->disc_all_services.cb_arg);
+
+    return 0;
+}
+
+static int
+ble_gatt_rx_err_disc_service_uuid(struct ble_gatt_entry *entry,
+                                  struct ble_hs_conn *conn,
+                                  struct ble_att_error_rsp *rsp)
+{
+    uint8_t status;
+
+    if (rsp->bhaep_error_code == BLE_ATT_ERR_ATTR_NOT_FOUND) {
+        /* Discovery is complete. */
+        status = 0;
+    } else {
+        /* Discovery failure. */
+        status = rsp->bhaep_error_code;
+    }
+
+    entry->disc_service_uuid.cb(conn->bhc_handle, status, NULL,
+                                entry->disc_service_uuid.cb_arg);
+
+    return 0;
+}
+
 void
-ble_gatt_rx_error(struct ble_hs_conn *conn, struct ble_att_error_rsp *rsp)
+ble_gatt_rx_err(struct ble_hs_conn *conn, struct ble_att_error_rsp *rsp)
 {
+    const struct ble_gatt_dispatch_entry *dispatch;
     struct ble_gatt_entry *entry;
     struct ble_gatt_entry *prev;
 
@@ -338,19 +453,10 @@ ble_gatt_rx_error(struct ble_hs_conn *conn, struct ble_att_error_rsp *rsp)
         return;
     }
 
-    switch (entry->op) {
-    case BLE_GATT_OP_NONE:
-    case BLE_GATT_OP_MTU:
-        break;
-
-    case BLE_GATT_OP_FIND_INFO:
-        /* XXX: Branch on error status. */
-        break;
+    dispatch = ble_gatt_dispatch_get(entry->op);
+    dispatch->rx_err_cb(entry, conn, rsp);
 
-    default:
-        assert(0);
-        break;
-    }
+    ble_gatt_entry_remove_free(entry, prev);
 }
 
 int
@@ -508,6 +614,57 @@ ble_gatt_rx_read_group_type_complete(struct ble_hs_conn *conn, int rc)
     }
 }
 
+void
+ble_gatt_rx_find_type_value_hinfo(struct ble_hs_conn *conn,
+                                  struct ble_att_clt_adata *adata)
+{
+    struct ble_gatt_service service;
+    struct ble_gatt_entry *entry;
+    struct ble_gatt_entry *prev;
+    int rc;
+
+    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_DISC_SERVICE_UUID, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    service.start_handle = adata->att_handle;
+    service.end_handle = adata->end_group_handle;
+    memcpy(service.uuid128, entry->disc_service_uuid.service_uuid, 16);
+
+    rc = entry->disc_service_uuid.cb(conn->bhc_handle, 0, &service,
+                                     entry->disc_service_uuid.cb_arg);
+    if (rc != 0) {
+        ble_gatt_entry_remove_free(entry, prev);
+    }
+}
+
+void
+ble_gatt_rx_find_type_value_complete(struct ble_hs_conn *conn, int rc)
+{
+    struct ble_gatt_entry *entry;
+    struct ble_gatt_entry *prev;
+
+    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_DISC_SERVICE_UUID, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    if (rc != 0 || entry->disc_service_uuid.prev_handle == 0xffff) {
+        /* Error or all services discovered. */
+        entry->disc_service_uuid.cb(conn->bhc_handle, rc, NULL,
+                                    entry->disc_service_uuid.cb_arg);
+        ble_gatt_entry_remove_free(entry, prev);
+    } else {
+        /* Send follow-up request. */
+        ble_gatt_entry_set_pending(entry);
+    }
+}
+
 int
 ble_gatt_disc_all_services(uint16_t conn_handle, ble_gatt_disc_service_fn *cb,
                            void *cb_arg)
@@ -527,6 +684,25 @@ ble_gatt_disc_all_services(uint16_t conn_handle, ble_gatt_disc_service_fn *cb,
     return 0;
 }
 
+int
+ble_gatt_disc_service_by_uuid(uint16_t conn_handle, void *service_uuid128,
+                              ble_gatt_disc_service_fn *cb, void *cb_arg)
+{
+    struct ble_gatt_entry *entry;
+    int rc;
+
+    rc = ble_gatt_new_entry(conn_handle, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+    entry->op = BLE_GATT_OP_DISC_SERVICE_UUID;
+    memcpy(entry->disc_service_uuid.service_uuid, service_uuid128, 16);
+    entry->disc_service_uuid.prev_handle = 0x0000;
+    entry->disc_service_uuid.cb = cb;
+    entry->disc_service_uuid.cb_arg = cb_arg;
+
+    return 0;
+}
 
 int
 ble_gatt_init(void)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/test/ble_gatt_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_test.c b/net/nimble/host/src/test/ble_gatt_test.c
index f4d23aa..ad9b4d0 100644
--- a/net/nimble/host/src/test/ble_gatt_test.c
+++ b/net/nimble/host/src/test/ble_gatt_test.c
@@ -36,8 +36,17 @@ struct ble_gatt_test_service {
 static struct ble_gatt_service
     ble_gatt_test_services[BLE_GATT_TEST_MAX_SERVICES];
 static int ble_gatt_test_num_services;
+static int ble_gatt_test_rx_complete;
 
-int
+static void
+ble_gatt_test_init(void)
+{
+    ble_hs_test_util_init();
+    ble_gatt_test_num_services = 0;
+    ble_gatt_test_rx_complete = 0;
+}
+
+static int
 ble_gatt_test_misc_service_length(struct ble_gatt_test_service *service)
 {
     if (service->uuid16 != 0) {
@@ -47,6 +56,32 @@ ble_gatt_test_misc_service_length(struct ble_gatt_test_service *service)
     }
 }
 
+static void
+ble_gatt_test_misc_rx_err_rsp(struct ble_hs_conn *conn, uint8_t req_op,
+                              uint8_t error_code)
+{
+    struct ble_att_error_rsp rsp;
+    struct ble_l2cap_chan *chan;
+    uint8_t buf[BLE_ATT_ERROR_RSP_SZ];
+    int rc;
+
+    /* Send the pending ATT Request. */
+    ble_gatt_wakeup();
+
+    rsp.bhaep_req_op = req_op;
+    rsp.bhaep_handle = conn->bhc_handle;
+    rsp.bhaep_error_code = error_code;
+
+    rc = ble_att_error_rsp_write(buf, sizeof buf, &rsp);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    chan = ble_hs_conn_chan_find(conn, BLE_L2CAP_CID_ATT);
+    TEST_ASSERT_FATAL(chan != NULL);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+}
+
 static int
 ble_gatt_test_misc_rx_disc_services_rsp_once(
     struct ble_hs_conn *conn, struct ble_gatt_test_service *services)
@@ -116,6 +151,67 @@ ble_gatt_test_misc_rx_disc_services_rsp(struct ble_hs_conn *conn,
                                                              services + idx);
         idx += count;
     }
+
+    if (services[idx - 1].start_handle != 0xffff) {
+        ble_gatt_test_misc_rx_err_rsp(conn, BLE_ATT_OP_READ_GROUP_TYPE_REQ,
+                                      BLE_ATT_ERR_ATTR_NOT_FOUND);
+    }
+}
+
+static int
+ble_gatt_test_misc_rx_disc_uuid_rsp_once(
+    struct ble_hs_conn *conn, struct ble_gatt_test_service *services)
+{
+    struct ble_l2cap_chan *chan;
+    uint8_t buf[1024];
+    int off;
+    int rc;
+    int i;
+
+    /* Send the pending ATT Find By Type Value Request. */
+    ble_gatt_wakeup();
+
+    buf[0] = BLE_ATT_OP_FIND_TYPE_VALUE_RSP;
+    off = BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ;
+    for (i = 0; ; i++) {
+        if (services[i].start_handle == 0) {
+            /* No more services. */
+            break;
+        }
+
+        htole16(buf + off, services[i].start_handle);
+        off += 2;
+
+        htole16(buf + off, services[i].end_handle);
+        off += 2;
+    }
+
+    chan = ble_hs_conn_chan_find(conn, BLE_L2CAP_CID_ATT);
+    TEST_ASSERT_FATAL(chan != NULL);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, off);
+    TEST_ASSERT(rc == 0);
+
+    return i;
+}
+
+static void
+ble_gatt_test_misc_rx_disc_uuid_rsp(struct ble_hs_conn *conn,
+                                    struct ble_gatt_test_service *services)
+{
+    int count;
+    int idx;
+
+    idx = 0;
+    while (services[idx].start_handle != 0) {
+        count = ble_gatt_test_misc_rx_disc_uuid_rsp_once(conn, services + idx);
+        idx += count;
+    }
+
+    if (services[idx - 1].start_handle != 0xffff) {
+        ble_gatt_test_misc_rx_err_rsp(conn, BLE_ATT_OP_FIND_TYPE_VALUE_REQ,
+                                      BLE_ATT_ERR_ATTR_NOT_FOUND);
+    }
 }
 
 static void
@@ -141,16 +237,21 @@ ble_gatt_test_misc_verify_services(struct ble_gatt_test_service *services)
     }
 
     TEST_ASSERT(i == ble_gatt_test_num_services);
+    TEST_ASSERT(ble_gatt_test_rx_complete);
 }
 
 static int
 ble_gatt_test_misc_disc_cb(uint16_t conn_handle, int status,
                            struct ble_gatt_service *service, void *arg)
 {
-    TEST_ASSERT_FATAL(ble_gatt_test_num_services < BLE_GATT_TEST_MAX_SERVICES);
     TEST_ASSERT(status == 0);
+    TEST_ASSERT(!ble_gatt_test_rx_complete);
 
-    if (status == 0) {
+    if (service == NULL) {
+        ble_gatt_test_rx_complete = 1;
+    } else {
+        TEST_ASSERT_FATAL(ble_gatt_test_num_services <
+                          BLE_GATT_TEST_MAX_SERVICES);
         ble_gatt_test_services[ble_gatt_test_num_services++] = *service;
     }
 
@@ -163,8 +264,7 @@ ble_gatt_test_misc_good_disc_services(struct ble_gatt_test_service *services)
     struct ble_hs_conn *conn;
     int rc;
 
-    ble_hs_test_util_init();
-    ble_gatt_test_num_services = 0;
+    ble_gatt_test_init();
 
     conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
 
@@ -175,7 +275,29 @@ ble_gatt_test_misc_good_disc_services(struct ble_gatt_test_service *services)
     ble_gatt_test_misc_verify_services(services);
 }
 
-TEST_CASE(ble_gatt_test_1)
+static void
+ble_gatt_test_misc_good_disc_uuid(struct ble_gatt_test_service *services)
+{
+    struct ble_hs_conn *conn;
+    int rc;
+
+    ble_gatt_test_init();
+
+    conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
+
+    if (services[0].uuid16 != 0) {
+        rc = ble_hs_uuid_from_16bit(services[0].uuid16, services[0].uuid128);
+        TEST_ASSERT_FATAL(rc == 0);
+    }
+    rc = ble_gatt_disc_service_by_uuid(2, services[0].uuid128,
+                                       ble_gatt_test_misc_disc_cb, NULL);
+    TEST_ASSERT(rc == 0);
+
+    ble_gatt_test_misc_rx_disc_uuid_rsp(conn, services);
+    ble_gatt_test_misc_verify_services(services);
+}
+
+TEST_CASE(ble_gatt_test_disc_all_services)
 {
     /*** One 128-bit service. */
     ble_gatt_test_misc_good_disc_services((struct ble_gatt_test_service[]) {
@@ -207,13 +329,80 @@ TEST_CASE(ble_gatt_test_1)
         { 0 }
     });
 
-    /* XXX: Test multiple responses. */
-    /* XXX: Test 16-bit UUIDs. */
+    /*** End with handle 0xffff. */
+    ble_gatt_test_misc_good_disc_services((struct ble_gatt_test_service[]) {
+        { 1, 5, 0,      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 7, 0xffff, 0, {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, },
+    });
+}
+
+TEST_CASE(ble_gatt_test_disc_service_uuid)
+{
+    /*** 128-bit service; one entry. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0,      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 0 }
+    });
+
+    /*** 128-bit service; two entries. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0,      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 8, 43, 0,     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 0 }
+    });
+
+    /*** 128-bit service; five entries. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0,      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 8, 43, 0,     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 67, 100, 0,   {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 102, 103, 0,  {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 262, 900, 0,  {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 0 }
+    });
+
+    /*** 128-bit service; end with handle 0xffff. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0,      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 7, 0xffff, 0, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 0 }
+    });
+
+    /*** 16-bit service; one entry. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0x1234 },
+        { 0 }
+    });
+
+    /*** 16-bit service; two entries. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0x1234 },
+        { 85, 243, 0x1234 },
+        { 0 }
+    });
+
+    /*** 16-bit service; five entries. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0x1234 },
+        { 85, 243, 0x1234 },
+        { 382, 383, 0x1234 },
+        { 562, 898, 0x1234 },
+        { 902, 984, 0x1234 },
+        { 0 }
+    });
+
+    /*** 16-bit service; end with handle 0xffff. */
+    ble_gatt_test_misc_good_disc_uuid((struct ble_gatt_test_service[]) {
+        { 1, 5, 0x1234 },
+        { 9, 0xffff, 0x1234 },
+        { 0 }
+    });
 }
 
 TEST_SUITE(ble_gatt_suite)
 {
-    ble_gatt_test_1();
+    ble_gatt_test_disc_all_services();
+    ble_gatt_test_disc_service_uuid();
 }
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/941319ca/net/nimble/host/src/test/ble_hs_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_att_svr_test.c b/net/nimble/host/src/test/ble_hs_att_svr_test.c
index 37f3f11..0b1d928 100644
--- a/net/nimble/host/src/test/ble_hs_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_hs_att_svr_test.c
@@ -622,7 +622,7 @@ TEST_CASE(ble_att_svr_test_find_type_value)
     struct ble_att_find_type_value_req req;
     struct ble_l2cap_chan *chan;
     struct ble_hs_conn *conn;
-    uint8_t buf[BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ + 2];
+    uint8_t buf[BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ + 2];
     uint16_t handle1;
     uint16_t handle2;
     uint16_t handle3;
@@ -649,7 +649,7 @@ TEST_CASE(ble_att_svr_test_find_type_value)
     /* One-time write of the attribute value at the end of the request. */
     ble_att_svr_test_attr_r_1 = (uint8_t[]){0x99, 0x99};
     ble_att_svr_test_attr_r_1_len = 2;
-    memcpy(buf + BLE_ATT_FIND_TYPE_VALUE_REQ_MIN_SZ,
+    memcpy(buf + BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ,
            ble_att_svr_test_attr_r_1,
            ble_att_svr_test_attr_r_1_len);