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 03:54:24 UTC

incubator-mynewt-larva git commit: GATT Discover All Primary Services procedure.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 3b84fcd48 -> f945610a8


GATT Discover All Primary Services procedure.


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

Branch: refs/heads/master
Commit: f945610a8b07b90f23b58ed88eeb738de16ef6ab
Parents: 3b84fcd
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Dec 3 18:54:13 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Dec 3 18:54:13 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gatt.h        |   4 +-
 net/nimble/host/include/host/ble_hs_test.h     |   1 +
 net/nimble/host/src/ble_att.c                  |   1 +
 net/nimble/host/src/ble_att.h                  |   1 +
 net/nimble/host/src/ble_att_clt.c              |  20 +--
 net/nimble/host/src/ble_att_cmd.h              |  36 ++---
 net/nimble/host/src/ble_gap_conn.c             |   1 +
 net/nimble/host/src/ble_gatt.c                 |  52 +++++--
 net/nimble/host/src/ble_hs.c                   |  17 ++-
 net/nimble/host/src/ble_hs_conn.c              |   4 +-
 net/nimble/host/src/ble_l2cap.c                |   4 +-
 net/nimble/host/src/test/ble_gatt_test.c       | 151 ++++++++++++++++++++
 net/nimble/host/src/test/ble_hs_att_clt_test.c |   7 +-
 net/nimble/host/src/test/ble_hs_test.c         |   1 +
 net/nimble/host/src/test/ble_hs_test_util.c    |   8 +-
 net/nimble/host/src/test/ble_hs_test_util.h    |   3 +-
 16 files changed, 259 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 de1fc73..7aa3c98 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -12,8 +12,8 @@ struct ble_gatt_service {
     uint8_t uuid128[16];
 };
 
-typedef int ble_gatt_disc_service_fn(uint16_t conn_handle,
-                                     struct ble_att_clt_adata *adata,
+typedef int ble_gatt_disc_service_fn(uint16_t conn_handle, int status,
+                                     struct ble_gatt_service *service,
                                      void *arg);
 
 int ble_gatt_disc_all_services(uint16_t conn_handle,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/include/host/ble_hs_test.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_hs_test.h b/net/nimble/host/include/host/ble_hs_test.h
index 520c051..76a43b2 100644
--- a/net/nimble/host/include/host/ble_hs_test.h
+++ b/net/nimble/host/include/host/ble_hs_test.h
@@ -28,5 +28,6 @@ int ble_host_hci_test_all(void);
 int ble_hs_conn_test_all(void);
 int ble_gap_test_all(void);
 int ble_hs_uuid_test_all(void);
+int ble_gatt_test_all(void);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 1f63ac6..71abd6f 100644
--- a/net/nimble/host/src/ble_att.c
+++ b/net/nimble/host/src/ble_att.c
@@ -37,6 +37,7 @@ static struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = {
     { BLE_ATT_OP_FIND_TYPE_VALUE_REQ,  ble_att_svr_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 },
     { BLE_ATT_OP_WRITE_REQ,            ble_att_svr_rx_write },
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 8a522b5..b8e6439 100644
--- a/net/nimble/host/src/ble_att.h
+++ b/net/nimble/host/src/ble_att.h
@@ -106,6 +106,7 @@ struct ble_att_svr_entry {
 struct ble_att_clt_adata {
     uint16_t att_handle;
     uint16_t end_group_handle;
+    int value_len;
     void *value;
 };
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 7fe36a6..05684b6 100644
--- a/net/nimble/host/src/ble_att_clt.c
+++ b/net/nimble/host/src/ble_att_clt.c
@@ -321,14 +321,18 @@ err:
 }
 
 static int
-ble_att_clt_parse_attribute_data(struct os_mbuf *om, int data_len,
+ble_att_clt_parse_attribute_data(struct os_mbuf **om, int data_len,
                                  struct ble_att_clt_adata *adata)
 {
-    /* XXX: Pull up om */
+    *om = os_mbuf_pullup(*om, data_len);
+    if (*om == NULL) {
+        return ENOMEM;
+    }
 
-    adata->att_handle = le16toh(om->om_data + 0);
-    adata->end_group_handle = le16toh(om->om_data + 2);
-    adata->value = om->om_data + 6;
+    adata->att_handle = le16toh((*om)->om_data + 0);
+    adata->end_group_handle = le16toh((*om)->om_data + 2);
+    adata->value_len = data_len - BLE_ATT_READ_GROUP_TYPE_ADATA_BASE_SZ;
+    adata->value = (*om)->om_data + BLE_ATT_READ_GROUP_TYPE_ADATA_BASE_SZ;
 
     return 0;
 }
@@ -342,7 +346,7 @@ ble_att_clt_rx_read_group_type_rsp(struct ble_hs_conn *conn,
     struct ble_att_clt_adata adata;
     int rc;
 
-    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_READ_GROUP_TYPE_REQ_BASE_SZ);
+    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
     if (*rxom == NULL) {
         return ENOMEM;
     }
@@ -353,12 +357,12 @@ ble_att_clt_rx_read_group_type_rsp(struct ble_hs_conn *conn,
         return rc;
     }
 
-    os_mbuf_adj(*rxom, BLE_ATT_READ_GROUP_TYPE_REQ_BASE_SZ);
+    os_mbuf_adj(*rxom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
 
     /* XXX: Verify group handle is valid. */
 
     while (OS_MBUF_PKTLEN(*rxom) > 0) {
-        rc = ble_att_clt_parse_attribute_data(*rxom, rsp.bhagp_length, &adata);
+        rc = ble_att_clt_parse_attribute_data(rxom, rsp.bhagp_length, &adata);
         if (rc != 0) {
             break;
         }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 f6ab782..443e684 100644
--- a/net/nimble/host/src/ble_att_cmd.h
+++ b/net/nimble/host/src/ble_att_cmd.h
@@ -187,6 +187,8 @@ struct ble_att_read_group_type_rsp {
     uint8_t bhagp_length;
 };
 
+#define BLE_ATT_READ_GROUP_TYPE_ADATA_BASE_SZ   4
+
 /**
  * | Parameter                          | Size (octets)     |
  * +------------------------------------+-------------------+
@@ -202,39 +204,39 @@ struct ble_att_write_req {
 #define BLE_ATT_WRITE_RSP_SZ             1
 
 int ble_att_error_rsp_parse(void *payload, int len,
-                               struct ble_att_error_rsp *rsp);
+                            struct ble_att_error_rsp *rsp);
 int ble_att_error_rsp_write(void *payload, int len,
-                               struct ble_att_error_rsp *rsp);
+                            struct ble_att_error_rsp *rsp);
 int ble_att_mtu_cmd_parse(void *payload, int len,
-                             struct ble_att_mtu_cmd *cmd);
+                          struct ble_att_mtu_cmd *cmd);
 int ble_att_mtu_req_write(void *payload, int len,
-                             struct ble_att_mtu_cmd *cmd);
+                          struct ble_att_mtu_cmd *cmd);
 int ble_att_mtu_rsp_write(void *payload, int len,
-                             struct ble_att_mtu_cmd *cmd);
+                          struct ble_att_mtu_cmd *cmd);
 int ble_att_find_info_req_parse(void *payload, int len,
-                                   struct ble_att_find_info_req *req);
+                                struct ble_att_find_info_req *req);
 int ble_att_find_info_req_write(void *payload, int len,
-                                   struct ble_att_find_info_req *req);
+                                struct ble_att_find_info_req *req);
 int ble_att_find_info_rsp_parse(void *payload, int len,
-                                   struct ble_att_find_info_rsp *rsp);
+                                struct ble_att_find_info_rsp *rsp);
 int ble_att_find_info_rsp_write(void *payload, int len,
-                                   struct ble_att_find_info_rsp *rsp);
+                                struct ble_att_find_info_rsp *rsp);
 int ble_att_find_type_value_req_parse(
     void *payload, int len, struct ble_att_find_type_value_req *req);
 int ble_att_find_type_value_req_write(
     void *payload, int len, struct ble_att_find_type_value_req *req);
 int ble_att_read_req_parse(void *payload, int len,
-                              struct ble_att_read_req *req);
+                           struct ble_att_read_req *req);
 int ble_att_read_req_write(void *payload, int len,
-                              struct ble_att_read_req *req);
+                           struct ble_att_read_req *req);
 int ble_att_read_type_req_parse(void *payload, int len,
-                                   struct ble_att_read_type_req *req);
+                                struct ble_att_read_type_req *req);
 int ble_att_read_type_req_write(void *payload, int len,
-                                   struct ble_att_read_type_req *req);
+                                struct ble_att_read_type_req *req);
 int ble_att_read_type_rsp_parse(void *payload, int len,
-                                   struct ble_att_read_type_rsp *rsp);
+                                struct ble_att_read_type_rsp *rsp);
 int ble_att_read_type_rsp_write(void *payload, int len,
-                                   struct ble_att_read_type_rsp *rsp);
+                                struct ble_att_read_type_rsp *rsp);
 int ble_att_read_group_type_req_parse(
     void *payload, int len, struct ble_att_read_group_type_req *req);
 int ble_att_read_group_type_req_write(
@@ -244,6 +246,6 @@ int ble_att_read_group_type_rsp_parse(
 int ble_att_read_group_type_rsp_write(
     void *payload, int len, struct ble_att_read_group_type_rsp *rsp);
 int ble_att_write_req_parse(void *payload, int len,
-                               struct ble_att_write_req *req);
+                            struct ble_att_write_req *req);
 int ble_att_write_req_write(void *payload, int len,
-                               struct ble_att_write_req *req);
+                            struct ble_att_write_req *req);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/ble_gap_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_conn.c b/net/nimble/host/src/ble_gap_conn.c
index 3a3953a..51dfcd3 100644
--- a/net/nimble/host/src/ble_gap_conn.c
+++ b/net/nimble/host/src/ble_gap_conn.c
@@ -362,6 +362,7 @@ ble_gap_conn_slave_in_progress(void)
 int
 ble_gap_conn_init(void)
 {
+    ble_gap_conn_cb = NULL;
     ble_gap_conn_master_state = BLE_GAP_CONN_STATE_IDLE;
     ble_gap_conn_slave_state = BLE_GAP_CONN_STATE_IDLE;
     memset(ble_gap_conn_addr_master, 0, sizeof ble_gap_conn_addr_master);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 207d03c..f472730 100644
--- a/net/nimble/host/src/ble_gatt.c
+++ b/net/nimble/host/src/ble_gatt.c
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <string.h>
 #include "os/os_mempool.h"
+#include "nimble/ble.h"
 #include "host/ble_gatt.h"
 #include "host/ble_hs.h"
 #include "ble_hs_uuid.h"
@@ -174,8 +175,8 @@ static void
 ble_gatt_entry_set_pending(struct ble_gatt_entry *entry)
 {
     assert(!(entry->flags & BLE_GATT_ENTRY_F_PENDING));
-    assert(!(entry->flags & BLE_GATT_ENTRY_F_EXPECTING));
 
+    entry->flags &= ~BLE_GATT_ENTRY_F_EXPECTING;
     entry->flags |= BLE_GATT_ENTRY_F_PENDING;
     ble_hs_kick_gatt();
 }
@@ -184,10 +185,10 @@ static void
 ble_gatt_entry_set_expecting(struct ble_gatt_entry *entry,
                              struct ble_gatt_entry *prev)
 {
-    assert(!(entry->flags & BLE_GATT_ENTRY_F_PENDING));
     assert(!(entry->flags & BLE_GATT_ENTRY_F_EXPECTING));
 
     ble_gatt_entry_remove(entry, prev);
+    entry->flags &= ~BLE_GATT_ENTRY_F_PENDING;
     entry->flags |= BLE_GATT_ENTRY_F_EXPECTING;
     STAILQ_INSERT_TAIL(&ble_gatt_list, entry, next);
 }
@@ -436,17 +437,51 @@ void
 ble_gatt_rx_read_group_type_adata(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;
+    uint16_t uuid16;
+    int cbrc;
+    int rc;
 
-    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_MTU, 1, NULL);
+    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_SERVICES, 1,
+                          &prev);
     if (entry == NULL) {
         /* Not expecting a response from this device. */
         return;
     }
 
+    switch (adata->value_len) {
+    case 2:
+        uuid16 = le16toh(adata->value);
+        rc = ble_hs_uuid_from_16bit(uuid16, service.uuid128);
+        if (rc != 0) {
+            goto done;
+        }
+        break;
+
+    case 16:
+        memcpy(service.uuid128, adata->value, 16);
+        break;
+
+    default:
+        rc = EMSGSIZE;
+        goto done;
+    }
+
     entry->disc_all_services.prev_handle = adata->end_group_handle;
 
-    /* XXX: Call success callback. */
+    service.start_handle = adata->att_handle;
+    service.end_handle = adata->end_group_handle;
+
+    rc = 0;
+
+done:
+    cbrc = entry->disc_all_services.cb(conn->bhc_handle, rc, &service,
+                                       entry->disc_all_services.cb_arg);
+    if (rc != 0 || cbrc != 0) {
+        ble_gatt_entry_remove_free(entry, prev);
+    }
 }
 
 void
@@ -455,15 +490,16 @@ ble_gatt_rx_read_group_type_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_MTU, 1, &prev);
+    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_SERVICES, 1,
+                          &prev);
     if (entry == NULL) {
         /* Not expecting a response from this device. */
         return;
     }
 
-    if (entry->disc_all_services.prev_handle == 0xffff) {
-        /* All services discovered. */
-        entry->disc_all_services.cb(conn->bhc_handle, NULL,
+    if (rc != 0 || entry->disc_all_services.prev_handle == 0xffff) {
+        /* Error or all services discovered. */
+        entry->disc_all_services.cb(conn->bhc_handle, rc, NULL,
                                     entry->disc_all_services.cb_arg);
         ble_gatt_entry_remove_free(entry, prev);
     } else {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/ble_hs.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index 450b678..8a9f1e5 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -42,7 +42,7 @@ os_stack_t ble_hs_stack[BLE_HS_STACK_SIZE];
 #define HCI_CMD_BUFS        (8)
 #define HCI_CMD_BUF_SIZE    (260)       /* XXX: temporary, Fix later */
 struct os_mempool g_hci_cmd_pool;
-os_membuf_t g_hci_cmd_buf[OS_MEMPOOL_SIZE(HCI_CMD_BUFS, HCI_CMD_BUF_SIZE)];
+os_membuf_t g_hci_cmd_buf[OS_MEMPOOL_BYTES(HCI_CMD_BUFS, HCI_CMD_BUF_SIZE)];
 
 /* XXX: this might be transport layer*/
 #define HCI_NUM_OS_EVENTS       (32)
@@ -55,13 +55,13 @@ os_membuf_t g_hci_cmd_buf[OS_MEMPOOL_SIZE(HCI_CMD_BUFS, HCI_CMD_BUF_SIZE)];
      sizeof(struct os_mbuf_pkthdr))
 
 #define BLE_HS_MBUF_MEMPOOL_SIZE                                 \
-    OS_MEMPOOL_SIZE(BLE_HS_NUM_MBUFS, BLE_HS_MBUF_MEMBLOCK_SIZE)
+    OS_MEMPOOL_BYTES(BLE_HS_NUM_MBUFS, BLE_HS_MBUF_MEMBLOCK_SIZE)
 
 #define BLE_HS_PKT_MAX              BLE_HS_NUM_MBUFS
 
 struct os_mempool g_hci_os_event_pool;
-os_membuf_t g_hci_os_event_buf[OS_MEMPOOL_SIZE(HCI_NUM_OS_EVENTS,
-                                               HCI_OS_EVENT_BUF_SIZE)];
+os_membuf_t g_hci_os_event_buf[OS_MEMPOOL_BYTES(HCI_NUM_OS_EVENTS,
+                                                HCI_OS_EVENT_BUF_SIZE)];
 
 static os_membuf_t *ble_hs_mbuf_mem;
 struct os_mempool ble_hs_mbuf_mempool;
@@ -295,8 +295,8 @@ ble_hs_init(uint8_t prio)
     }
 
     ble_hs_pkt_mem = malloc(
-        OS_MEMPOOL_SIZE(BLE_HS_PKT_MAX,
-                        sizeof (struct ble_hs_pkt)));
+        OS_MEMPOOL_BYTES(BLE_HS_PKT_MAX,
+                         sizeof (struct ble_hs_pkt)));
     if (ble_hs_pkt_mem == NULL) {
         rc = ENOMEM;
         goto err;
@@ -336,6 +336,11 @@ ble_hs_init(uint8_t prio)
         goto err;
     }
 
+    rc = ble_gatt_init();
+    if (rc != 0) {
+        goto err;
+    }
+
     ble_hs_kick_hci_ev.ev_queued = 0;
     ble_hs_kick_hci_ev.ev_type = BLE_HS_KICK_HCI_EVENT;
     ble_hs_kick_hci_ev.ev_arg = NULL;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/ble_hs_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.c b/net/nimble/host/src/ble_hs_conn.c
index d72a956..7a7b9e7 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -155,8 +155,8 @@ ble_hs_conn_init(void)
     ble_hs_conn_free_mem();
 
     ble_hs_conn_elem_mem = malloc(
-        OS_MEMPOOL_SIZE(BLE_HS_CONN_MAX,
-                        sizeof (struct ble_hs_conn)));
+        OS_MEMPOOL_BYTES(BLE_HS_CONN_MAX,
+                         sizeof (struct ble_hs_conn)));
     if (ble_hs_conn_elem_mem == NULL) {
         rc = ENOMEM;
         goto err;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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 5237222..b2a4795 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -220,8 +220,8 @@ ble_l2cap_init(void)
     ble_l2cap_free_mem();
 
     ble_l2cap_chan_mem = malloc(
-        OS_MEMPOOL_SIZE(BLE_L2CAP_CHAN_MAX,
-                        sizeof (struct ble_l2cap_chan)));
+        OS_MEMPOOL_BYTES(BLE_L2CAP_CHAN_MAX,
+                         sizeof (struct ble_l2cap_chan)));
     if (ble_l2cap_chan_mem == NULL) {
         rc = ENOMEM;
         goto err;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/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
new file mode 100644
index 0000000..f3c3e2c
--- /dev/null
+++ b/net/nimble/host/src/test/ble_gatt_test.c
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "testutil/testutil.h"
+#include "nimble/ble.h"
+#include "host/ble_hs_test.h"
+#include "host/ble_gatt.h"
+#include "ble_att_cmd.h"
+#include "ble_hs_conn.h"
+#include "ble_hs_test_util.h"
+
+#define BLE_GATT_TEST_MAX_SERVICES  256
+static struct ble_gatt_service
+    ble_gatt_test_services[BLE_GATT_TEST_MAX_SERVICES];
+static int ble_gatt_test_num_services;
+
+static void
+ble_gatt_test_misc_rx_disc_services_rsp(struct ble_hs_conn *conn,
+                                        struct ble_gatt_service *services)
+{
+    struct ble_att_read_group_type_rsp rsp;
+    struct ble_l2cap_chan *chan;
+    uint8_t buf[1024];
+    int off;
+    int rc;
+    int i;
+
+    rsp.bhagp_length = 20;
+    rc = ble_att_read_group_type_rsp_write(
+        buf, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ, &rsp);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    off = BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ;
+    for (i = 0; services[i].start_handle != 0; i++) {
+        htole16(buf + off, services[i].start_handle);
+        off += 2;
+
+        htole16(buf + off, services[i].end_handle);
+        off += 2;
+
+        memcpy(buf + off, services[i].uuid128, 16);
+        off += 16;
+    }
+
+    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);
+}
+
+static void
+ble_gatt_test_misc_verify_services(struct ble_gatt_service *services)
+{
+    int i;
+
+    for (i = 0; services[i].start_handle != 0; i++) {
+        TEST_ASSERT(memcmp(services + i, ble_gatt_test_services + i,
+                           sizeof *services) == 0);
+    }
+
+    TEST_ASSERT(i == ble_gatt_test_num_services);
+}
+
+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);
+
+    if (status == 0) {
+        ble_gatt_test_services[ble_gatt_test_num_services++] = *service;
+    }
+
+    return 0;
+}
+
+static void
+ble_gatt_test_misc_successful_disc_services(struct ble_gatt_service *services)
+{
+    struct ble_hs_conn *conn;
+    int rc;
+
+    ble_hs_test_util_init();
+    ble_gatt_test_num_services = 0;
+
+    conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
+
+    rc = ble_gatt_disc_all_services(2, ble_gatt_test_misc_disc_cb, NULL);
+    TEST_ASSERT(rc == 0);
+    ble_gatt_wakeup();
+
+    ble_gatt_test_misc_rx_disc_services_rsp(conn, services);
+    ble_gatt_test_misc_verify_services(services);
+}
+
+TEST_CASE(ble_gatt_test_1)
+{
+    /*** One service. */
+    ble_gatt_test_misc_successful_disc_services((struct ble_gatt_service[]) {
+        { 1, 5,     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 0 }
+    });
+
+    /*** Two services. */
+    ble_gatt_test_misc_successful_disc_services((struct ble_gatt_service[]) {
+        { 1, 5,     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 10, 50,   {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, },
+        { 0 }
+    });
+
+    /*** Five services. */
+    ble_gatt_test_misc_successful_disc_services((struct ble_gatt_service[]) {
+        { 1, 5,     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, },
+        { 10, 50,   {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, },
+        { 123, 678,  {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, },
+        { 0 }
+    });
+
+    /* XXX: Test multiple responses. */
+    /* XXX: Test 16-bit UUIDs. */
+}
+
+TEST_SUITE(ble_gatt_suite)
+{
+    ble_gatt_test_1();
+}
+
+int
+ble_gatt_test_all(void)
+{
+    ble_gatt_suite();
+
+    return tu_any_failed;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/test/ble_hs_att_clt_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_att_clt_test.c b/net/nimble/host/src/test/ble_hs_att_clt_test.c
index a2a7c5e..d4c9ed2 100644
--- a/net/nimble/host/src/test/ble_hs_att_clt_test.c
+++ b/net/nimble/host/src/test/ble_hs_att_clt_test.c
@@ -26,14 +26,11 @@
 
 static void
 ble_att_clt_test_misc_init(struct ble_hs_conn **conn,
-                              struct ble_l2cap_chan **att_chan)
+                           struct ble_l2cap_chan **att_chan)
 {
     ble_hs_test_util_init();
 
-    ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
-    *conn = ble_hs_conn_find(2);
-    TEST_ASSERT_FATAL(*conn != NULL);
-
+    *conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
     *att_chan = ble_hs_conn_chan_find(*conn, BLE_L2CAP_CID_ATT);
     TEST_ASSERT_FATAL(*att_chan != NULL);
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/test/ble_hs_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test.c b/net/nimble/host/src/test/ble_hs_test.c
index 81a5a5a..ed379ec 100644
--- a/net/nimble/host/src/test/ble_hs_test.c
+++ b/net/nimble/host/src/test/ble_hs_test.c
@@ -46,6 +46,7 @@ main(void)
     ble_hs_conn_test_all();
     ble_gap_test_all();
     ble_hs_uuid_test_all();
+    ble_gatt_test_all();
 
     return tu_any_failed;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/test/ble_hs_test_util.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test_util.c b/net/nimble/host/src/test/ble_hs_test_util.c
index 53974dd..488e0d4 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.c
+++ b/net/nimble/host/src/test/ble_hs_test_util.c
@@ -57,10 +57,11 @@ ble_hs_test_util_build_cmd_status(uint8_t *dst, int len,
     htole16(dst + 4, opcode);
 }
 
-void
+struct ble_hs_conn *
 ble_hs_test_util_create_conn(uint16_t handle, uint8_t *addr)
 {
     struct hci_le_conn_complete evt;
+    struct ble_hs_conn *conn;
     int rc;
 
     rc = ble_gap_direct_connection_establishment(0, addr);
@@ -76,6 +77,11 @@ ble_hs_test_util_create_conn(uint16_t handle, uint8_t *addr)
     evt.connection_handle = 2;
     memcpy(evt.peer_addr, addr, 6);
     rc = ble_gap_conn_rx_conn_complete(&evt);
+
+    conn = ble_hs_conn_find(handle);
+    TEST_ASSERT_FATAL(conn != NULL);
+
+    return conn;
 }
 
 void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/f945610a/net/nimble/host/src/test/ble_hs_test_util.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test_util.h b/net/nimble/host/src/test/ble_hs_test_util.h
index 31c1e81..bd58636 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.h
+++ b/net/nimble/host/src/test/ble_hs_test_util.h
@@ -29,7 +29,8 @@ void ble_hs_test_util_build_cmd_complete(uint8_t *dst, int len,
 void ble_hs_test_util_build_cmd_status(uint8_t *dst, int len,
                                        uint8_t status, uint8_t num_pkts,
                                        uint16_t opcode);
-void ble_hs_test_util_create_conn(uint16_t handle, uint8_t *addr);
+struct ble_hs_conn *ble_hs_test_util_create_conn(uint16_t handle,
+                                                 uint8_t *addr);
 void ble_hs_test_util_rx_ack(uint16_t opcode, uint8_t status);
 void ble_hs_test_util_rx_le_ack(uint16_t ocf, uint8_t status);
 int ble_hs_test_util_l2cap_rx_payload_flat(struct ble_hs_conn *conn,