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/24 00:34:30 UTC

[1/6] incubator-mynewt-larva git commit: Separate gatt source into gatt-client, gatt-server

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 63761d721 -> ba4a7ed2b


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/test/ble_gatt_disc_s_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_disc_s_test.c b/net/nimble/host/src/test/ble_gatt_disc_s_test.c
index c2187ca..640e29d 100644
--- a/net/nimble/host/src/test/ble_gatt_disc_s_test.c
+++ b/net/nimble/host/src/test/ble_gatt_disc_s_test.c
@@ -68,7 +68,7 @@ ble_gatt_disc_s_test_misc_rx_all_rsp_once(
     int i;
 
     /* Send the pending ATT Read By Group Type Request. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
 
     rsp.bagp_length = ble_gatt_disc_s_test_misc_svc_length(services);
     rc = ble_att_read_group_type_rsp_write(
@@ -128,7 +128,7 @@ ble_gatt_disc_s_test_misc_rx_all_rsp(
 
     if (services[idx - 1].end_handle != 0xffff) {
         /* Send the pending ATT Request. */
-        ble_gatt_wakeup();
+        ble_gattc_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_GROUP_TYPE_REQ,
                                         BLE_ATT_ERR_ATTR_NOT_FOUND,
                                         services[idx - 1].start_handle);
@@ -146,7 +146,7 @@ ble_gatt_disc_s_test_misc_rx_uuid_rsp_once(
     int i;
 
     /* Send the pending ATT Find By Type Value Request. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
 
     buf[0] = BLE_ATT_OP_FIND_TYPE_VALUE_RSP;
     off = BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ;
@@ -188,7 +188,7 @@ ble_gatt_disc_s_test_misc_rx_uuid_rsp(
 
     if (services[idx - 1].end_handle != 0xffff) {
         /* Send the pending ATT Request. */
-        ble_gatt_wakeup();
+        ble_gattc_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_FIND_TYPE_VALUE_REQ,
                                         BLE_ATT_ERR_ATTR_NOT_FOUND,
                                         services[idx - 1].start_handle);
@@ -251,7 +251,7 @@ ble_gatt_disc_s_test_misc_good_all(struct ble_gatt_disc_s_test_svc *services)
     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_disc_s_test_misc_disc_cb,
-                                    NULL);
+                                     NULL);
     TEST_ASSERT(rc == 0);
 
     ble_gatt_disc_s_test_misc_rx_all_rsp(conn, services);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/test/ble_gatt_read_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_read_test.c b/net/nimble/host/src/test/ble_gatt_read_test.c
index 88cec80..f6c6e52 100644
--- a/net/nimble/host/src/test/ble_gatt_read_test.c
+++ b/net/nimble/host/src/test/ble_gatt_read_test.c
@@ -60,7 +60,7 @@ ble_gatt_read_test_misc_rx_rsp_good(struct ble_hs_conn *conn,
     TEST_ASSERT_FATAL(attr->value_len <= sizeof buf);
 
     /* Send the pending ATT Read Request. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
 
     buf[0] = BLE_ATT_OP_READ_RSP;
     memcpy(buf + 1, attr->value, attr->value_len);
@@ -79,7 +79,7 @@ ble_gatt_read_test_misc_rx_rsp_bad(struct ble_hs_conn *conn,
                                    uint8_t att_error)
 {
     /* Send the pending ATT Read Request. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
 
     ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_REQ, att_error,
                                     attr->handle);
@@ -168,7 +168,7 @@ TEST_CASE(ble_gatt_read_test_by_handle)
         } });
 }
 
-TEST_SUITE(gle_gatt_read_test_suite)
+TEST_SUITE(ble_gatt_read_test_suite)
 {
     ble_gatt_read_test_by_handle();
 }
@@ -176,7 +176,7 @@ TEST_SUITE(gle_gatt_read_test_suite)
 int
 ble_gatt_read_test_all(void)
 {
-    gle_gatt_read_test_suite();
+    ble_gatt_read_test_suite();
 
     return tu_any_failed;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/test/ble_gatt_write_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_write_test.c b/net/nimble/host/src/test/ble_gatt_write_test.c
index 141348f..ce52b2e 100644
--- a/net/nimble/host/src/test/ble_gatt_write_test.c
+++ b/net/nimble/host/src/test/ble_gatt_write_test.c
@@ -83,7 +83,7 @@ TEST_CASE(ble_gatt_write_test_no_rsp)
     TEST_ASSERT(rc == 0);
 
     /* Send the pending ATT Write Command. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
 
     /* No response expected; verify callback got called. */
@@ -105,7 +105,7 @@ TEST_CASE(ble_gatt_write_test_rsp)
     TEST_ASSERT(rc == 0);
 
     /* Send the pending ATT Write Command. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
 
     /* Response not received yet; verify callback not called. */


[4/6] incubator-mynewt-larva git commit: Change function type for ATT callbacks.

Posted by cc...@apache.org.
Change function type for ATT callbacks.


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

Branch: refs/heads/master
Commit: 936b9e83adcf34f1359f688e0fda0f3dabf71f91
Parents: e949b8a
Author: Christopher Collins <cc...@gmail.com>
Authored: Wed Dec 23 14:00:33 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Wed Dec 23 15:33:49 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_att.h      | 25 ++----
 net/nimble/host/include/host/ble_gatt.h     |  4 +-
 net/nimble/host/src/ble_att_priv.h          | 11 +++
 net/nimble/host/src/ble_att_svr.c           | 71 +++++++++--------
 net/nimble/host/src/ble_gatts.c             | 18 ++++-
 net/nimble/host/src/test/ble_att_svr_test.c | 97 +++++++++++++-----------
 project/hostctlrtest/src/main.c             | 44 +++++------
 7 files changed, 147 insertions(+), 123 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/net/nimble/host/include/host/ble_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_att.h b/net/nimble/host/include/host/ble_att.h
index e4c8112..6522f0d 100644
--- a/net/nimble/host/include/host/ble_att.h
+++ b/net/nimble/host/include/host/ble_att.h
@@ -58,16 +58,16 @@
 
 #define BLE_ATT_ATTR_MAX_LEN                512
 
-union ble_att_svr_handle_arg {
+union ble_att_svr_access_ctxt {
     struct {
         void *attr_data;
         int attr_len;
-    } aha_read;
+    } ahc_read;
 
     struct {
         void *attr_data;
         int attr_len;
-    } aha_write;
+    } ahc_write;
 };
 
 #define HA_FLAG_PERM_READ                   (1 << 0)
@@ -77,8 +77,6 @@ union ble_att_svr_handle_arg {
 #define HA_FLAG_AUTHENTICATION_REQ          (1 << 4)
 #define HA_FLAG_AUTHORIZATION_REQ           (1 << 5)
 
-struct ble_att_svr_entry;
-
 /**
  * Handles a host attribute request.
  *
@@ -91,21 +89,12 @@ struct ble_att_svr_entry;
  *                              One of the BLE_ATT_ERR_[...] codes on
  *                                  failure.
  */
-typedef int ble_att_svr_handle_func(struct ble_att_svr_entry *entry,
+typedef int ble_att_svr_access_fn(uint16_t handle_id, uint8_t *uuid128,
                                     uint8_t op,
-                                    union ble_att_svr_handle_arg *arg);
-
-struct ble_att_svr_entry {
-    STAILQ_ENTRY(ble_att_svr_entry) ha_next;
-
-    uint8_t ha_uuid[16];
-    uint8_t ha_flags;
-    uint8_t ha_pad1;
-    uint16_t ha_handle_id;
-    ble_att_svr_handle_func *ha_fn;
-};
+                                    union ble_att_svr_access_ctxt *ctxt,
+                                    void *arg);
 
 int ble_att_svr_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
-                         ble_att_svr_handle_func *fn);
+                         ble_att_svr_access_fn *cb, void *cb_arg);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/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 cbb0338..665b138 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -74,13 +74,13 @@ int ble_gatt_init(void);
 /*** @server. */
 struct ble_gatt_desc_def {
     uint8_t *uuid128;
-    ble_att_svr_handle_func *access_cb;
+    ble_att_svr_access_fn *access_cb;
 };
 
 struct ble_gatt_char_def {
     uint8_t properties;
     uint8_t *uuid128;
-    ble_att_svr_handle_func *access_cb;
+    ble_att_svr_access_fn *access_cb;
 };
 
 #define BLE_GATT_SVC_TYPE_END       0

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/net/nimble/host/src/ble_att_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_priv.h b/net/nimble/host/src/ble_att_priv.h
index 94a73aa..9de76ad 100644
--- a/net/nimble/host/src/ble_att_priv.h
+++ b/net/nimble/host/src/ble_att_priv.h
@@ -49,6 +49,17 @@ struct ble_att_svr_conn {
     uint32_t basc_prep_write_rx_time;
 };
 
+struct ble_att_svr_entry {
+    STAILQ_ENTRY(ble_att_svr_entry) ha_next;
+
+    uint8_t ha_uuid[16];
+    uint8_t ha_flags;
+    uint8_t ha_pad1;
+    uint16_t ha_handle_id;
+    ble_att_svr_access_fn *ha_cb;
+    void *ha_cb_arg;
+};
+
 /**
  * Called from ble_att_svr_walk().  Called on each entry in the 
  * ble_att_svr_list.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/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 eb301eb..17393f8 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -131,7 +131,7 @@ ble_att_svr_next_id(void)
  */
 int
 ble_att_svr_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
-                     ble_att_svr_handle_func *fn)
+                     ble_att_svr_access_fn *cb, void *cb_arg)
 {
     struct ble_att_svr_entry *entry;
 
@@ -143,7 +143,8 @@ ble_att_svr_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
     memcpy(&entry->ha_uuid, uuid, sizeof entry->ha_uuid);
     entry->ha_flags = flags;
     entry->ha_handle_id = ble_att_svr_next_id();
-    entry->ha_fn = fn;
+    entry->ha_cb = cb;
+    entry->ha_cb_arg = cb_arg;
 
     ble_att_svr_list_lock();
     STAILQ_INSERT_TAIL(&ble_att_svr_list, entry, ha_next);
@@ -751,7 +752,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
                             struct os_mbuf *rxom, struct os_mbuf *txom,
                             uint16_t mtu)
 {
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     struct ble_att_svr_entry *ha;
     uint16_t uuid16;
     uint16_t first;
@@ -783,15 +784,16 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
              */
             uuid16 = ble_hs_uuid_16bit(ha->ha_uuid);
             if (uuid16 == req->bavq_attr_type) {
-                rc = ha->ha_fn(ha, BLE_ATT_OP_READ_REQ, &arg);
+                rc = ha->ha_cb(ha->ha_handle_id, ha->ha_uuid,
+                               BLE_ATT_OP_READ_REQ, &arg, ha->ha_cb_arg);
                 if (rc != 0) {
                     rc = BLE_HS_EAPP;
                     goto done;
                 }
                 rc = os_mbuf_memcmp(rxom,
                                     BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ,
-                                    arg.aha_read.attr_data,
-                                    arg.aha_read.attr_len);
+                                    arg.ahc_read.attr_data,
+                                    arg.ahc_read.attr_len);
                 if (rc == 0) {
                     match = 1;
                 }
@@ -955,7 +957,7 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
                              uint16_t *err_handle)
 {
     struct ble_att_read_type_rsp rsp;
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     struct ble_att_svr_entry *entry;
     struct os_mbuf *txom;
     uint8_t *dptr;
@@ -1007,17 +1009,18 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
         if (entry->ha_handle_id >= req->batq_start_handle &&
             entry->ha_handle_id <= req->batq_end_handle) {
 
-            rc = entry->ha_fn(entry, BLE_ATT_OP_READ_REQ, &arg);
+            rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
+                              BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
             if (rc != 0) {
                 *att_err = BLE_ATT_ERR_UNLIKELY;
                 *err_handle = entry->ha_handle_id;
                 goto done;
             }
 
-            if (arg.aha_read.attr_len > ble_l2cap_chan_mtu(chan) - 4) {
+            if (arg.ahc_read.attr_len > ble_l2cap_chan_mtu(chan) - 4) {
                 attr_len = ble_l2cap_chan_mtu(chan) - 4;
             } else {
-                attr_len = arg.aha_read.attr_len;
+                attr_len = arg.ahc_read.attr_len;
             }
 
             if (prev_attr_len == 0) {
@@ -1040,7 +1043,7 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
             }
 
             htole16(dptr + 0, entry->ha_handle_id);
-            memcpy(dptr + 2, arg.aha_read.attr_data, attr_len);
+            memcpy(dptr + 2, arg.ahc_read.attr_data, attr_len);
         }
     }
 
@@ -1190,7 +1193,7 @@ int
 ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                     struct os_mbuf **rxom)
 {
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     struct ble_att_svr_entry *entry;
     struct ble_att_read_req req;
     uint16_t err_handle;
@@ -1222,14 +1225,15 @@ ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         goto err;
     }
 
-    if (entry->ha_fn == NULL) {
+    if (entry->ha_cb == NULL) {
         att_err = BLE_ATT_ERR_UNLIKELY;
         err_handle = req.barq_handle;
         rc = BLE_HS_ENOTSUP;
         goto err;
     }
 
-    rc = entry->ha_fn(entry, BLE_ATT_OP_READ_REQ, &arg);
+    rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
+                      BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
     if (rc != 0) {
         att_err = BLE_ATT_ERR_UNLIKELY;
         err_handle = req.barq_handle;
@@ -1237,8 +1241,8 @@ ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         goto err;
     }
 
-    rc = ble_att_svr_tx_read_rsp(conn, chan, arg.aha_read.attr_data,
-                                 arg.aha_read.attr_len, &att_err);
+    rc = ble_att_svr_tx_read_rsp(conn, chan, arg.ahc_read.attr_data,
+                                 arg.ahc_read.attr_len, &att_err);
     if (rc != 0) {
         err_handle = req.barq_handle;
         goto err;
@@ -1267,22 +1271,23 @@ static int
 ble_att_svr_service_uuid(struct ble_att_svr_entry *entry, uint16_t *uuid16,
                          uint8_t *uuid128)
 {
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     int rc;
 
-    rc = entry->ha_fn(entry, BLE_ATT_OP_READ_REQ, &arg);
+    rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
+                      BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
     if (rc != 0) {
         return rc;
     }
 
-    switch (arg.aha_read.attr_len) {
+    switch (arg.ahc_read.attr_len) {
     case 16:
         *uuid16 = 0;
-        memcpy(uuid128, arg.aha_read.attr_data, 16);
+        memcpy(uuid128, arg.ahc_read.attr_data, 16);
         return 0;
 
     case 2:
-        *uuid16 = le16toh(arg.aha_read.attr_data);
+        *uuid16 = le16toh(arg.ahc_read.attr_data);
         if (*uuid16 == 0) {
             return BLE_HS_EINVAL;
         }
@@ -1605,7 +1610,7 @@ int
 ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                      struct os_mbuf **rxom)
 {
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     struct ble_att_svr_entry *entry;
     struct ble_att_write_req req;
     uint16_t err_handle;
@@ -1634,18 +1639,19 @@ ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         goto err;
     }
 
-    if (entry->ha_fn == NULL) {
+    if (entry->ha_cb == NULL) {
         att_err = BLE_ATT_ERR_UNLIKELY;
         err_handle = req.bawq_handle;
         rc = BLE_HS_ENOTSUP;
         goto err;
     }
 
-    arg.aha_write.attr_data = ble_att_svr_flat_buf;
-    arg.aha_write.attr_len = OS_MBUF_PKTLEN(*rxom);
-    os_mbuf_copydata(*rxom, 0, arg.aha_write.attr_len,
-                     arg.aha_write.attr_data);
-    att_err = entry->ha_fn(entry, BLE_ATT_OP_WRITE_REQ, &arg);
+    arg.ahc_write.attr_data = ble_att_svr_flat_buf;
+    arg.ahc_write.attr_len = OS_MBUF_PKTLEN(*rxom);
+    os_mbuf_copydata(*rxom, 0, arg.ahc_write.attr_len,
+                     arg.ahc_write.attr_data);
+    att_err = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
+                           BLE_ATT_OP_WRITE_REQ, &arg, entry->ha_cb_arg);
     if (att_err != 0) {
         err_handle = req.bawq_handle;
         rc = BLE_HS_EAPP;
@@ -1773,7 +1779,7 @@ ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
 static int
 ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
 {
-    union ble_att_svr_handle_arg arg;
+    union ble_att_svr_access_ctxt arg;
     struct ble_att_prep_entry *entry;
     struct ble_att_prep_entry *next;
     struct ble_att_svr_entry *attr;
@@ -1809,9 +1815,10 @@ ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
                 return BLE_ATT_ERR_INVALID_HANDLE;
             }
 
-            arg.aha_write.attr_data = ble_att_svr_flat_buf;
-            arg.aha_write.attr_len = buf_off;
-            rc = attr->ha_fn(attr, BLE_ATT_OP_WRITE_REQ, &arg);
+            arg.ahc_write.attr_data = ble_att_svr_flat_buf;
+            arg.ahc_write.attr_len = buf_off;
+            rc = attr->ha_cb(attr->ha_handle_id, attr->ha_uuid,
+                             BLE_ATT_OP_WRITE_REQ, &arg, attr->ha_cb_arg);
             if (rc != 0) {
                 *err_handle = entry->bape_handle;
                 return BLE_ATT_ERR_UNLIKELY;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/net/nimble/host/src/ble_gatts.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatts.c b/net/nimble/host/src/ble_gatts.c
index 8dbc435..7740610 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -15,6 +15,7 @@
  */
 
 #include <stddef.h>
+#include <assert.h>
 #include "ble_hs_priv.h"
 #include "ble_gatt_priv.h"
 
@@ -30,9 +31,17 @@ static struct ble_gatts_svc_entry
 static int ble_gatts_num_svc_entries;
 
 static int
-ble_gatts_svc_access(struct ble_att_svr_entry *entry, uint8_t op,
-                     union ble_att_svr_handle_arg *arg)
+ble_gatts_svc_access(uint16_t handle_id, uint8_t *uuid128, uint8_t op,
+                     union ble_att_svr_access_ctxt *ctxt, void *arg)
 {
+    const struct ble_gatt_svc_def *svc;
+
+    assert(op == BLE_ATT_OP_READ_REQ);
+
+    svc = arg;
+    ctxt->ahc_read.attr_data = svc->uuid128;
+    ctxt->ahc_read.attr_len = 16;
+
     return 0;
 }
 
@@ -70,8 +79,11 @@ ble_gatts_register_service(const struct ble_gatt_svc_def *svc,
         return BLE_HS_EAGAIN;
     }
 
+    /* Register service definition attribute (cast away const on callback
+     * arg).
+     */
     rc = ble_att_svr_register(svc->uuid128, HA_FLAG_PERM_READ, out_handle,
-                              ble_gatts_svc_access);
+                              ble_gatts_svc_access, (void *)svc);
     if (rc != 0) {
         return rc;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/net/nimble/host/src/test/ble_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_svr_test.c b/net/nimble/host/src/test/ble_att_svr_test.c
index 0854641..45720fe 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -57,14 +57,15 @@ ble_att_svr_test_misc_init(struct ble_hs_conn **conn,
 }
 
 static int
-ble_att_svr_test_misc_attr_fn_r_1(struct ble_att_svr_entry *entry,
-                                     uint8_t op,
-                                     union ble_att_svr_handle_arg *arg)
+ble_att_svr_test_misc_attr_fn_r_1(uint16_t handle_id, uint8_t *uuid128,
+                                  uint8_t op,
+                                  union ble_att_svr_access_ctxt *ctxt,
+                                  void *arg)
 {
     switch (op) {
     case BLE_ATT_OP_READ_REQ:
-        arg->aha_read.attr_data = ble_att_svr_test_attr_r_1;
-        arg->aha_read.attr_len = ble_att_svr_test_attr_r_1_len;
+        ctxt->ahc_read.attr_data = ble_att_svr_test_attr_r_1;
+        ctxt->ahc_read.attr_len = ble_att_svr_test_attr_r_1_len;
         return 0;
 
     default:
@@ -73,13 +74,15 @@ ble_att_svr_test_misc_attr_fn_r_1(struct ble_att_svr_entry *entry,
 }
 
 static int
-ble_att_svr_test_misc_attr_fn_r_2(struct ble_att_svr_entry *entry, uint8_t op,
-                                  union ble_att_svr_handle_arg *arg)
+ble_att_svr_test_misc_attr_fn_r_2(uint16_t handle_id, uint8_t *uuid128,
+                                  uint8_t op,
+                                  union ble_att_svr_access_ctxt *ctxt,
+                                  void *arg)
 {
     switch (op) {
     case BLE_ATT_OP_READ_REQ:
-        arg->aha_read.attr_data = ble_att_svr_test_attr_r_2;
-        arg->aha_read.attr_len = ble_att_svr_test_attr_r_2_len;
+        ctxt->ahc_read.attr_data = ble_att_svr_test_attr_r_2;
+        ctxt->ahc_read.attr_len = ble_att_svr_test_attr_r_2_len;
         return 0;
 
     default:
@@ -88,9 +91,10 @@ ble_att_svr_test_misc_attr_fn_r_2(struct ble_att_svr_entry *entry, uint8_t op,
 }
 
 static int
-ble_att_svr_test_misc_attr_fn_r_group(struct ble_att_svr_entry *entry,
+ble_att_svr_test_misc_attr_fn_r_group(uint16_t handle_id, uint8_t *uuid128,
                                       uint8_t op,
-                                      union ble_att_svr_handle_arg *arg)
+                                      union ble_att_svr_access_ctxt *ctxt,
+                                      void *arg)
 {
     /* Service 0x1122 from 1 to 5 */
     /* Service 0x2233 from 6 to 10 */
@@ -124,13 +128,13 @@ ble_att_svr_test_misc_attr_fn_r_group(struct ble_att_svr_entry *entry,
         return -1;
     }
 
-    TEST_ASSERT_FATAL(entry->ha_handle_id >= 1 && entry->ha_handle_id <= 22);
+    TEST_ASSERT_FATAL(handle_id >= 1 && handle_id <= 22);
 
-    arg->aha_read.attr_data = vals + entry->ha_handle_id;
-    if (memcmp(arg->aha_read.attr_data + 2, zeros, 14) == 0) {
-        arg->aha_read.attr_len = 2;
+    ctxt->ahc_read.attr_data = vals + handle_id;
+    if (memcmp(ctxt->ahc_read.attr_data + 2, zeros, 14) == 0) {
+        ctxt->ahc_read.attr_len = 2;
     } else {
-        arg->aha_read.attr_len = 16;
+        ctxt->ahc_read.attr_len = 16;
     }
 
     return 0;
@@ -139,12 +143,12 @@ ble_att_svr_test_misc_attr_fn_r_group(struct ble_att_svr_entry *entry,
 static void
 ble_att_svr_test_misc_register_uuid128(uint8_t *uuid128, uint8_t flags,
                                        uint16_t expected_handle,
-                                       ble_att_svr_handle_func *fn)
+                                       ble_att_svr_access_fn *fn)
 {
     uint16_t handle;
     int rc;
 
-    rc = ble_att_svr_register(uuid128, flags, &handle, fn);
+    rc = ble_att_svr_register(uuid128, flags, &handle, fn, NULL);
     TEST_ASSERT_FATAL(rc == 0);
     TEST_ASSERT_FATAL(handle == expected_handle);
 }
@@ -152,7 +156,7 @@ ble_att_svr_test_misc_register_uuid128(uint8_t *uuid128, uint8_t flags,
 static void
 ble_att_svr_test_misc_register_uuid16(uint16_t uuid16, uint8_t flags,
                                       uint16_t expected_handle,
-                                      ble_att_svr_handle_func *fn)
+                                      ble_att_svr_access_fn *fn)
 {
     uint8_t uuid128[16];
     int rc;
@@ -217,14 +221,16 @@ ble_att_svr_test_misc_register_group_attrs(void)
 }
 
 static int
-ble_att_svr_test_misc_attr_fn_w_1(struct ble_att_svr_entry *entry, uint8_t op,
-                                  union ble_att_svr_handle_arg *arg)
+ble_att_svr_test_misc_attr_fn_w_1(uint16_t handle_id, uint8_t *uuid128,
+                                  uint8_t op,
+                                  union ble_att_svr_access_ctxt *ctxt,
+                                  void *arg)
 {
     switch (op) {
     case BLE_ATT_OP_WRITE_REQ:
-        memcpy(ble_att_svr_test_attr_w_1, arg->aha_write.attr_data,
-               arg->aha_write.attr_len);
-        ble_att_svr_test_attr_w_1_len = arg->aha_write.attr_len;
+        memcpy(ble_att_svr_test_attr_w_1, ctxt->ahc_write.attr_data,
+               ctxt->ahc_write.attr_len);
+        ble_att_svr_test_attr_w_1_len = ctxt->ahc_write.attr_len;
         return 0;
 
     default:
@@ -233,14 +239,16 @@ ble_att_svr_test_misc_attr_fn_w_1(struct ble_att_svr_entry *entry, uint8_t op,
 }
 
 static int
-ble_att_svr_test_misc_attr_fn_w_2(struct ble_att_svr_entry *entry, uint8_t op,
-                                  union ble_att_svr_handle_arg *arg)
+ble_att_svr_test_misc_attr_fn_w_2(uint16_t handle_id, uint8_t *uuid128,
+                                  uint8_t op,
+                                  union ble_att_svr_access_ctxt *ctxt,
+                                  void *arg)
 {
     switch (op) {
     case BLE_ATT_OP_WRITE_REQ:
-        memcpy(ble_att_svr_test_attr_w_2, arg->aha_write.attr_data,
-               arg->aha_write.attr_len);
-        ble_att_svr_test_attr_w_2_len = arg->aha_write.attr_len;
+        memcpy(ble_att_svr_test_attr_w_2, ctxt->ahc_write.attr_data,
+               ctxt->ahc_write.attr_len);
+        ble_att_svr_test_attr_w_2_len = ctxt->ahc_write.attr_len;
         return 0;
 
     default:
@@ -705,7 +713,7 @@ TEST_CASE(ble_att_svr_test_read)
     ble_att_svr_test_attr_r_1 = (uint8_t[]){0,1,2,3,4,5,6,7};
     ble_att_svr_test_attr_r_1_len = 8;
     rc = ble_att_svr_register(uuid, 0, &req.barq_handle,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     rc = ble_att_read_req_write(buf, sizeof buf, &req);
@@ -727,9 +735,8 @@ TEST_CASE(ble_att_svr_test_read)
 
     rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
     TEST_ASSERT(rc == 0);
-    ble_att_svr_test_misc_verify_tx_read_rsp(chan,
-                                                ble_att_svr_test_attr_r_1,
-                                                BLE_ATT_MTU_DFLT - 1);
+    ble_att_svr_test_misc_verify_tx_read_rsp(chan, ble_att_svr_test_attr_r_1,
+                                             BLE_ATT_MTU_DFLT - 1);
 }
 
 TEST_CASE(ble_att_svr_test_write)
@@ -756,7 +763,7 @@ TEST_CASE(ble_att_svr_test_write)
 
     /*** Successful write. */
     rc = ble_att_svr_register(uuid, 0, &req.bawq_handle,
-                                 ble_att_svr_test_misc_attr_fn_w_1);
+                              ble_att_svr_test_misc_attr_fn_w_1, NULL);
     TEST_ASSERT(rc == 0);
 
     rc = ble_att_write_req_write(buf, sizeof buf, &req);
@@ -831,7 +838,7 @@ TEST_CASE(ble_att_svr_test_find_info)
 
     /*** Range too late. */
     rc = ble_att_svr_register(uuid1, 0, &handle1,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bafq_start_handle = 200;
@@ -863,9 +870,8 @@ TEST_CASE(ble_att_svr_test_find_info)
         } }));
 
     /*** Two 128-bit entries. */
-    rc = ble_att_svr_register(uuid2, 0,
-                                 &handle2,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+    rc = ble_att_svr_register(uuid2, 0, &handle2,
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bafq_start_handle = handle1;
@@ -888,9 +894,8 @@ TEST_CASE(ble_att_svr_test_find_info)
         } }));
 
     /*** Two 128-bit entries; 16-bit entry doesn't get sent. */
-    rc = ble_att_svr_register(uuid3, 0,
-                                 &handle3,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+    rc = ble_att_svr_register(uuid3, 0, &handle3,
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bafq_start_handle = handle1;
@@ -1008,7 +1013,7 @@ TEST_CASE(ble_att_svr_test_find_type_value)
 
     /*** Range too late. */
     rc = ble_att_svr_register(uuid1, 0, &handle1,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bavq_start_handle = 200;
@@ -1042,7 +1047,7 @@ TEST_CASE(ble_att_svr_test_find_type_value)
 
     /*** One entry, two attributes. */
     rc = ble_att_svr_register(uuid1, 0, &handle2,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bavq_start_handle = handle1;
@@ -1063,11 +1068,11 @@ TEST_CASE(ble_att_svr_test_find_type_value)
 
     /*** Entry 1: two attributes; entry 2: one attribute. */
     rc = ble_att_svr_register(uuid2, 0, &handle3,
-                                 ble_att_svr_test_misc_attr_fn_r_2);
+                              ble_att_svr_test_misc_attr_fn_r_2, NULL);
     TEST_ASSERT(rc == 0);
 
     rc = ble_att_svr_register(uuid1, 0, &handle4,
-                                 ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
     TEST_ASSERT(rc == 0);
 
     req.bavq_start_handle = 0x0001;
@@ -1114,7 +1119,7 @@ TEST_CASE(ble_att_svr_test_find_type_value)
 
     /*** Ensure attribute with wrong type is not included. */
     rc = ble_att_svr_register(uuid3, 0, &handle5,
-                              ble_att_svr_test_misc_attr_fn_r_1);
+                              ble_att_svr_test_misc_attr_fn_r_1, NULL);
 
     req.bavq_start_handle = 0x0001;
     req.bavq_end_handle = 0xffff;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/936b9e83/project/hostctlrtest/src/main.c
----------------------------------------------------------------------
diff --git a/project/hostctlrtest/src/main.c b/project/hostctlrtest/src/main.c
index ff9ddf2..be88129 100755
--- a/project/hostctlrtest/src/main.c
+++ b/project/hostctlrtest/src/main.c
@@ -203,19 +203,19 @@ static uint16_t hostctlrtest_char2_handle;
 static uint16_t hostctlrtest_data2_handle;
 
 static int
-hostctlrtest_attr_cb(struct ble_att_svr_entry *entry, uint8_t op,
-                     union ble_att_svr_handle_arg *arg)
+hostctlrtest_attr_cb(uint16_t handle_id, uint8_t *uuid128, uint8_t op,
+                     union ble_att_svr_handle_ctxt *ctxt, void *arg)
 {
     static uint8_t buf[128];
 
     assert(op == BLE_ATT_OP_READ_REQ);
 
-    if (entry->ha_handle_id == hostctlrtest_service_handle) {
+    if (handle_id == hostctlrtest_service_handle) {
         console_printf("reading service declaration");
         htole16(buf, 0x1234);
-        arg->aha_read.attr_data = buf;
-        arg->aha_read.attr_len = 2;
-    } else if (entry->ha_handle_id == hostctlrtest_char1_handle) {
+        ctxt->ahc_read.attr_data = buf;
+        ctxt->ahc_read.attr_len = 2;
+    } else if (handle_id == hostctlrtest_char1_handle) {
         console_printf("reading characteristic1 declaration");
 
         /* Properties. */
@@ -227,14 +227,14 @@ hostctlrtest_attr_cb(struct ble_att_svr_entry *entry, uint8_t op,
         /* UUID. */
         htole16(buf + 3, 0x5656);
 
-        arg->aha_read.attr_data = buf;
-        arg->aha_read.attr_len = 5;
-    } else if (entry->ha_handle_id == hostctlrtest_data1_handle) {
+        ctxt->ahc_read.attr_data = buf;
+        ctxt->ahc_read.attr_len = 5;
+    } else if (handle_id == hostctlrtest_data1_handle) {
         console_printf("reading characteristic1 value");
         memcpy(buf, "char1", 5);
-        arg->aha_read.attr_data = buf;
-        arg->aha_read.attr_len = 5;
-    } else if (entry->ha_handle_id == hostctlrtest_char2_handle) {
+        ctxt->ahc_read.attr_data = buf;
+        ctxt->ahc_read.attr_len = 5;
+    } else if (handle_id == hostctlrtest_char2_handle) {
         console_printf("reading characteristic2 declaration");
 
         /* Properties. */
@@ -246,13 +246,13 @@ hostctlrtest_attr_cb(struct ble_att_svr_entry *entry, uint8_t op,
         /* UUID. */
         htole16(buf + 3, 0x6767);
 
-        arg->aha_read.attr_data = buf;
-        arg->aha_read.attr_len = 5;
-    } else if (entry->ha_handle_id == hostctlrtest_data2_handle) {
+        ctxt->ahc_read.attr_data = buf;
+        ctxt->ahc_read.attr_len = 5;
+    } else if (handle_id == hostctlrtest_data2_handle) {
         console_printf("reading characteristic2 value");
         memcpy(buf, "char2", 5);
-        arg->aha_read.attr_data = buf;
-        arg->aha_read.attr_len = 5;
+        ctxt->ahc_read.attr_data = buf;
+        ctxt->ahc_read.attr_len = 5;
     } else {
         assert(0);
     }
@@ -270,33 +270,33 @@ hostctlrtest_register_attrs(void)
     rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_PRIMARY_SERVICE, uuid128);
     assert(rc == 0);
     rc = ble_att_svr_register(uuid128, 0, &hostctlrtest_service_handle,
-                              hostctlrtest_attr_cb);
+                              hostctlrtest_attr_cb, NULL);
     assert(rc == 0);
 
     /* Characteristic 1 (UUID=0x5656).*/
     rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
     assert(rc == 0);
     rc = ble_att_svr_register(uuid128, 0, &hostctlrtest_char1_handle,
-                              hostctlrtest_attr_cb);
+                              hostctlrtest_attr_cb, NULL);
     assert(rc == 0);
 
     rc = ble_hs_uuid_from_16bit(0x5656, uuid128);
     assert(rc == 0);
     rc = ble_att_svr_register(uuid128, 0, &hostctlrtest_data1_handle,
-                              hostctlrtest_attr_cb);
+                              hostctlrtest_attr_cb, NULL);
     assert(rc == 0);
 
     /* Characteristic 2 (UUID=0x6767).*/
     rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
     assert(rc == 0);
     rc = ble_att_svr_register(uuid128, 0, &hostctlrtest_char2_handle,
-                              hostctlrtest_attr_cb);
+                              hostctlrtest_attr_cb, NULL);
     assert(rc == 0);
 
     rc = ble_hs_uuid_from_16bit(0x6767, uuid128);
     assert(rc == 0);
     rc = ble_att_svr_register(uuid128, 0, &hostctlrtest_data2_handle,
-                              hostctlrtest_attr_cb);
+                              hostctlrtest_attr_cb, NULL);
     assert(rc == 0);
 }
 #endif


[6/6] incubator-mynewt-larva git commit: Fix bug: ATT server silent when read-type done!

Posted by cc...@apache.org.
Fix bug: ATT server silent when read-type done!


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

Branch: refs/heads/master
Commit: ba4a7ed2b053c74533e1f7c4e8a5c60c2938c893
Parents: 51bd2b4
Author: Christopher Collins <cc...@gmail.com>
Authored: Wed Dec 23 14:50:30 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Wed Dec 23 15:33:49 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/src/ble_att_svr.c           |  20 +-
 net/nimble/host/src/test/ble_att_svr_test.c | 252 ++++++++++++++++++++++-
 2 files changed, 256 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/ba4a7ed2/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 932e064..4975d65 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -961,14 +961,16 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
     struct ble_att_svr_entry *entry;
     struct os_mbuf *txom;
     uint8_t *dptr;
+    int entry_written;
     int txomlen;
     int prev_attr_len;
     int attr_len;
     int rc;
 
     *att_err = 0;    /* Silence unnecessary warning. */
-    *err_handle = 0; /* Silence unnecessary warning. */
 
+    *err_handle = req->batq_start_handle;
+    entry_written = 0;
     prev_attr_len = 0;
 
     txom = ble_att_get_pkthdr();
@@ -1006,9 +1008,7 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
             break;
         }
 
-        if (entry->ha_handle_id >= req->batq_start_handle &&
-            entry->ha_handle_id <= req->batq_end_handle) {
-
+        if (entry->ha_handle_id >= req->batq_start_handle) {
             rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
                               BLE_ATT_ACCESS_OP_READ, &arg, entry->ha_cb_arg);
             if (rc != 0) {
@@ -1044,11 +1044,12 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
 
             htole16(dptr + 0, entry->ha_handle_id);
             memcpy(dptr + 2, arg.ahc_read.attr_data, attr_len);
+            entry_written = 1;
         }
     }
 
 done:
-    if (OS_MBUF_PKTLEN(txom) == 0) {
+    if (!entry_written) {
         /* No matching attributes. */
         if (*att_err == 0) {
             *att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
@@ -1098,6 +1099,15 @@ ble_att_svr_rx_read_type(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     rc = ble_att_read_type_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
     assert(rc == 0);
 
+    if (req.batq_start_handle > req.batq_end_handle ||
+        req.batq_start_handle == 0) {
+
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.batq_start_handle;
+        rc = BLE_HS_EBADDATA;
+        goto err;
+    }
+
     switch ((*rxom)->om_len) {
     case BLE_ATT_READ_TYPE_REQ_SZ_16:
         uuid16 = le16toh((*rxom)->om_data + 5);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/ba4a7ed2/net/nimble/host/src/test/ble_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_svr_test.c b/net/nimble/host/src/test/ble_att_svr_test.c
index 3562210..4b7372f 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -102,21 +102,21 @@ ble_att_svr_test_misc_attr_fn_r_group(uint16_t handle_id, uint8_t *uuid128,
 
     static uint8_t vals[20][16] = {
         [1] =   { 0x22, 0x11 },
-        [2] =   { 0xdd, 0xdd },
-        [3] =   { 0xdd, 0xdd },
-        [4] =   { 0xdd, 0xdd },
-        [5] =   { 0xdd, 0xdd },
+        [2] =   { 0x01, 0x11 },
+        [3] =   { 0x02, 0x11 },
+        [4] =   { 0x03, 0x11 },
+        [5] =   { 0x04, 0x11 },
         [6] =   { 0x33, 0x22 },
-        [7] =   { 0xee, 0xee },
-        [8] =   { 0xee, 0xee },
-        [9] =   { 0xee, 0xee },
-        [10] =  { 0xee, 0xee },
+        [7] =   { 0x01, 0x22 },
+        [8] =   { 0x02, 0x22 },
+        [9] =   { 0x03, 0x22 },
+        [10] =  { 0x04, 0x22 },
         [11] =  { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
-        [12] =  { 0xdd, 0xdd },
+        [12] =  { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
         [13] =  { 0xdd, 0xdd },
-        [14] =  { 0xdd, 0xdd },
+        [14] =  { 0x55, 0x55 },
         [15] =  { 0xdd, 0xdd },
-        [16] =  { 0xdd, 0xdd },
+        [16] =  { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 },
         [17] =  { 0xdd, 0xdd },
         [18] =  { 0xdd, 0xdd },
         [19] =  { 0xdd, 0xdd },
@@ -544,6 +544,56 @@ ble_att_svr_test_misc_verify_tx_read_group_type_rsp(
     TEST_ASSERT(off == OS_MBUF_PKTLEN(om));
 }
 
+struct ble_att_svr_test_type_entry {
+    uint16_t handle;  /* 0 on last entry */
+    void *value;
+    int value_len;
+};
+
+/** Returns the number of entries successfully verified. */
+static void
+ble_att_svr_test_misc_verify_tx_read_type_rsp(
+    struct ble_l2cap_chan *chan,
+    struct ble_att_svr_test_type_entry *entries)
+{
+    struct ble_att_svr_test_type_entry *entry;
+    struct ble_att_read_type_rsp rsp;
+    struct os_mbuf *om;
+    uint16_t handle;
+    uint8_t buf[512];
+    int off;
+    int rc;
+
+    ble_hs_process_tx_data_queue();
+
+    om = os_mbuf_pullup(ble_hs_test_util_prev_tx,
+                        BLE_ATT_READ_TYPE_RSP_BASE_SZ);
+    TEST_ASSERT_FATAL(om != NULL);
+
+    rc = ble_att_read_type_rsp_parse(om->om_data, om->om_len, &rsp);
+    TEST_ASSERT(rc == 0);
+
+    off = BLE_ATT_READ_TYPE_RSP_BASE_SZ;
+    for (entry = entries; entry->handle != 0; entry++) {
+        TEST_ASSERT_FATAL(rsp.batp_length ==
+                          BLE_ATT_READ_TYPE_ADATA_BASE_SZ + entry->value_len);
+
+        rc = os_mbuf_copydata(om, off, 2, &handle);
+        TEST_ASSERT(rc == 0);
+        handle = le16toh(&handle);
+        TEST_ASSERT(handle == entry->handle);
+        off += 2;
+
+        rc = os_mbuf_copydata(om, off, entry->value_len, buf);
+        TEST_ASSERT(rc == 0);
+        TEST_ASSERT(memcmp(entry->value, buf, entry->value_len) == 0);
+        off += entry->value_len;
+    }
+
+    /* Ensure there is no extra data in the response. */
+    TEST_ASSERT(off == OS_MBUF_PKTLEN(om));
+}
+
 static void
 ble_att_svr_test_misc_verify_tx_prep_write_rsp(struct ble_l2cap_chan *chan,
                                                uint16_t attr_handle,
@@ -1140,6 +1190,185 @@ TEST_CASE(ble_att_svr_test_find_type_value)
         } }));
 }
 
+TEST_CASE(ble_att_svr_test_read_type)
+{
+    struct ble_att_read_type_req req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    uint8_t buf[BLE_ATT_READ_TYPE_REQ_SZ_16];
+    int rc;
+
+    ble_att_svr_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;
+
+    /*** Start handle of 0. */
+    req.batq_start_handle = 0;
+    req.batq_end_handle = 0;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_PRIMARY_SERVICE);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_att_svr_test_misc_verify_tx_err_rsp(
+        chan, BLE_ATT_OP_READ_TYPE_REQ, 0,
+        BLE_ATT_ERR_INVALID_HANDLE);
+
+    /*** Start handle > end handle. */
+    req.batq_start_handle = 101;
+    req.batq_end_handle = 100;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_PRIMARY_SERVICE);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_att_svr_test_misc_verify_tx_err_rsp(
+        chan, BLE_ATT_OP_READ_TYPE_REQ, 101,
+        BLE_ATT_ERR_INVALID_HANDLE);
+
+    /*** No attributes. */
+    req.batq_start_handle = 1;
+    req.batq_end_handle = 0xffff;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_PRIMARY_SERVICE);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_att_svr_test_misc_verify_tx_err_rsp(
+        chan, BLE_ATT_OP_READ_TYPE_REQ, 1,
+        BLE_ATT_ERR_ATTR_NOT_FOUND);
+
+    /*** Range too late. */
+    ble_att_svr_test_misc_register_group_attrs();
+    req.batq_start_handle = 200;
+    req.batq_end_handle = 300;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_PRIMARY_SERVICE);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_att_svr_test_misc_verify_tx_err_rsp(
+        chan, BLE_ATT_OP_READ_TYPE_REQ, 200,
+        BLE_ATT_ERR_ATTR_NOT_FOUND);
+
+    /*** One characteristic from one service. */
+    req.batq_start_handle = 1;
+    req.batq_end_handle = 2;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_CHARACTERISTIC);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+    ble_att_svr_test_misc_verify_tx_read_type_rsp(chan,
+        ((struct ble_att_svr_test_type_entry[]) { {
+            .handle = 2,
+            .value = (uint8_t[]){ 0x01, 0x11 },
+            .value_len = 2,
+        }, {
+            .handle = 0,
+        } }));
+
+    /*** Both characteristics from one service. */
+    req.batq_start_handle = 1;
+    req.batq_end_handle = 10;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_CHARACTERISTIC);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+    ble_att_svr_test_misc_verify_tx_read_type_rsp(chan,
+        ((struct ble_att_svr_test_type_entry[]) { {
+            .handle = 2,
+            .value = (uint8_t[]){ 0x01, 0x11 },
+            .value_len = 2,
+        }, {
+            .handle = 4,
+            .value = (uint8_t[]){ 0x03, 0x11 },
+            .value_len = 2,
+        }, {
+            .handle = 0,
+        } }));
+
+    /*** Ensure 16-bit and 128-bit values are retrieved separately. */
+    req.batq_start_handle = 11;
+    req.batq_end_handle = 0xffff;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_CHARACTERISTIC);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+    ble_att_svr_test_misc_verify_tx_read_type_rsp(chan,
+        ((struct ble_att_svr_test_type_entry[]) { {
+            .handle = 12,
+            .value = (uint8_t[]){ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
+            .value_len = 16,
+        }, {
+            .handle = 0,
+        } }));
+
+    req.batq_start_handle = 13;
+    req.batq_end_handle = 0xffff;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_CHARACTERISTIC);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+    ble_att_svr_test_misc_verify_tx_read_type_rsp(chan,
+        ((struct ble_att_svr_test_type_entry[]) { {
+            .handle = 14,
+            .value = (uint8_t[]){ 0x55, 0x55 },
+            .value_len = 2,
+        }, {
+            .handle = 0,
+        } }));
+
+    req.batq_start_handle = 15;
+    req.batq_end_handle = 0xffff;
+
+    rc = ble_att_read_type_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    htole16(buf + BLE_ATT_READ_TYPE_REQ_BASE_SZ,
+            BLE_ATT_UUID_CHARACTERISTIC);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+    ble_att_svr_test_misc_verify_tx_read_type_rsp(chan,
+        ((struct ble_att_svr_test_type_entry[]) { {
+            .handle = 16,
+            .value = (uint8_t[]){ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 },
+            .value_len = 16,
+        }, {
+            .handle = 0,
+        } }));
+}
+
 TEST_CASE(ble_att_svr_test_read_group_type)
 {
     struct ble_att_read_group_type_req req;
@@ -1443,6 +1672,7 @@ TEST_SUITE(ble_att_svr_suite)
     ble_att_svr_test_write();
     ble_att_svr_test_find_info();
     ble_att_svr_test_find_type_value();
+    ble_att_svr_test_read_type();
     ble_att_svr_test_read_group_type();
     ble_att_svr_test_prep_write();
 }


[2/6] incubator-mynewt-larva git commit: Separate gatt source into gatt-client, gatt-server

Posted by cc...@apache.org.
Separate gatt source into gatt-client,gatt-server


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

Branch: refs/heads/master
Commit: 2c857eba2561329e5d146c9362a9722ddebd483a
Parents: 63761d7
Author: Christopher Collins <cc...@gmail.com>
Authored: Wed Dec 23 11:00:53 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Wed Dec 23 15:33:27 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gatt.h         |    2 +-
 net/nimble/host/src/ble_att_clt.c               |   22 +-
 net/nimble/host/src/ble_gap_conn.c              |    2 +-
 net/nimble/host/src/ble_gatt.c                  | 1285 +----------------
 net/nimble/host/src/ble_gatt_priv.h             |   56 +-
 net/nimble/host/src/ble_gattc.c                 | 1320 ++++++++++++++++++
 net/nimble/host/src/ble_hs.c                    |    4 +-
 net/nimble/host/src/ble_hs_conn.c               |    2 +-
 net/nimble/host/src/test/ble_gatt_conn_test.c   |    8 +-
 net/nimble/host/src/test/ble_gatt_disc_c_test.c |    6 +-
 net/nimble/host/src/test/ble_gatt_disc_s_test.c |   10 +-
 net/nimble/host/src/test/ble_gatt_read_test.c   |    8 +-
 net/nimble/host/src/test/ble_gatt_write_test.c  |    4 +-
 13 files changed, 1415 insertions(+), 1314 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 04996e4..7b59d26 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -66,7 +66,7 @@ int ble_gatt_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
 int ble_gatt_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
                    uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg);
 
-int ble_gatt_mtu(uint16_t conn_handle);
+int ble_gatt_exchange_mtu(uint16_t conn_handle);
 int ble_gatt_init(void);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 ad93428..5bf9ddf 100644
--- a/net/nimble/host/src/ble_att_clt.c
+++ b/net/nimble/host/src/ble_att_clt.c
@@ -84,7 +84,7 @@ ble_att_clt_rx_error(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         return rc;
     }
 
-    ble_gatt_rx_err(conn->bhc_handle, &rsp);
+    ble_gattc_rx_err(conn->bhc_handle, &rsp);
 
     return 0;
 }
@@ -141,7 +141,7 @@ ble_att_clt_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
     ble_att_set_peer_mtu(chan, rsp.bamc_mtu);
 
-    ble_gatt_rx_mtu(conn, ble_l2cap_chan_mtu(chan));
+    ble_gattc_rx_mtu(conn, ble_l2cap_chan_mtu(chan));
 
     return 0;
 }
@@ -344,13 +344,13 @@ ble_att_clt_rx_read_type(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
             goto done;
         }
 
-        ble_gatt_rx_read_type_adata(conn, &adata);
+        ble_gattc_rx_read_type_adata(conn, &adata);
         os_mbuf_adj(*rxom, rsp.batp_length);
     }
 
 done:
     /* Notify GATT that the response is done being parsed. */
-    ble_gatt_rx_read_type_complete(conn, rc);
+    ble_gattc_rx_read_type_complete(conn, rc);
 
     return 0;
 
@@ -420,7 +420,7 @@ ble_att_clt_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     rc = 0;
 
 done:
-    ble_gatt_rx_read_rsp(conn, rc, value, value_len);
+    ble_gattc_rx_read_rsp(conn, rc, value, value_len);
     return rc;
 }
 
@@ -520,13 +520,13 @@ ble_att_clt_rx_read_group_type(struct ble_hs_conn *conn,
             goto done;
         }
 
-        ble_gatt_rx_read_group_type_adata(conn, &adata);
+        ble_gattc_rx_read_group_type_adata(conn, &adata);
         os_mbuf_adj(*rxom, rsp.bagp_length);
     }
 
 done:
     /* Notify GATT that the response is done being parsed. */
-    ble_gatt_rx_read_group_type_complete(conn, rc);
+    ble_gattc_rx_read_group_type_complete(conn, rc);
 
     return 0;
 }
@@ -616,12 +616,12 @@ ble_att_clt_rx_find_type_value(struct ble_hs_conn *conn,
             break;
         }
 
-        ble_gatt_rx_find_type_value_hinfo(conn, &adata);
+        ble_gattc_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);
+    /* Notify GATT client that the full response has been parsed. */
+    ble_gattc_rx_find_type_value_complete(conn, rc);
 
     return 0;
 }
@@ -712,7 +712,7 @@ ble_att_clt_rx_write(struct ble_hs_conn *conn,
                      struct os_mbuf **rxom)
 {
     /* No payload. */
-    ble_gatt_rx_write_rsp(conn);
+    ble_gattc_rx_write_rsp(conn);
 
     return 0;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 456411d..e853b5d 100644
--- a/net/nimble/host/src/ble_gap_conn.c
+++ b/net/nimble/host/src/ble_gap_conn.c
@@ -290,7 +290,7 @@ ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
 
     ble_gap_conn_notify_terminate(evt->connection_handle, evt->status,
                                   evt->reason);
-    ble_gatt_connection_broken(evt->connection_handle);
+    ble_gattc_connection_broken(evt->connection_handle);
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 d092d6f..de2766a 100644
--- a/net/nimble/host/src/ble_gatt.c
+++ b/net/nimble/host/src/ble_gatt.c
@@ -1,894 +1,18 @@
-/**
- * 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 <stddef.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include "os/os_mempool.h"
-#include "nimble/ble.h"
 #include "ble_gatt_priv.h"
-#include "ble_hs_priv.h"
-#include "host/ble_hs_uuid.h"
-#include "ble_hs_conn.h"
-#include "ble_att_cmd.h"
-#include "ble_att_priv.h"
-
-struct ble_gatt_entry {
-    STAILQ_ENTRY(ble_gatt_entry) next;
-
-    uint8_t op;
-    uint8_t flags;
-    uint16_t conn_handle;
-    union {
-        struct {
-            int (*cb)(int status, uint16_t conn_handle, uint16_t mtu,
-                      void *arg);
-            void *cb_arg;
-        } mtu;
-
-        struct {
-            uint16_t prev_handle;
-            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;
-
-        struct {
-            uint16_t prev_handle;
-            uint16_t end_handle;
-            ble_gatt_chr_fn *cb;
-            void *cb_arg;
-        } disc_all_chars;
-
-        struct {
-            uint16_t handle;
-            ble_gatt_attr_fn *cb;
-            void *cb_arg;
-        } read;
-
-        struct {
-            struct ble_gatt_attr attr;
-            ble_gatt_attr_fn *cb;
-            void *cb_arg;
-        } write;
-    };
-};
-
-#define BLE_GATT_RETX_PERIOD                    1000 /* Milliseconds. */
-
-#define BLE_GATT_OP_NONE                        UINT8_MAX
-#define BLE_GATT_OP_MTU                         0
-#define BLE_GATT_OP_DISC_ALL_SERVICES           1
-#define BLE_GATT_OP_DISC_SERVICE_UUID           2
-#define BLE_GATT_OP_DISC_ALL_CHARS              3
-#define BLE_GATT_OP_READ                        4
-#define BLE_GATT_OP_WRITE_NO_RSP                5
-#define BLE_GATT_OP_WRITE                       6
-#define BLE_GATT_OP_MAX                         7
-
-static struct os_callout_func ble_gatt_retx_timer;
-
-typedef int ble_gatt_kick_fn(struct ble_gatt_entry *entry);
-typedef void ble_gatt_err_fn(struct ble_gatt_entry *entry, int status);
-
-static int ble_gatt_kick_mtu(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_kick_disc_all_chars(struct ble_gatt_entry *entry);
-static int ble_gatt_kick_read(struct ble_gatt_entry *entry);
-static int ble_gatt_kick_write_no_rsp(struct ble_gatt_entry *entry);
-static int ble_gatt_kick_write(struct ble_gatt_entry *entry);
-
-static void ble_gatt_err_mtu(struct ble_gatt_entry *entry, int status);
-static void ble_gatt_err_disc_all_services(struct ble_gatt_entry *entry,
-                                           int status);
-static void ble_gatt_err_disc_service_uuid(struct ble_gatt_entry *entry,
-                                           int status);
-static void ble_gatt_err_disc_all_chars(struct ble_gatt_entry *entry,
-                                        int status);
-static void ble_gatt_err_read(struct ble_gatt_entry *entry, int status);
-static void ble_gatt_err_write(struct ble_gatt_entry *entry, int status);
-
-struct ble_gatt_dispatch_entry {
-    ble_gatt_kick_fn *kick_cb;
-    ble_gatt_err_fn *err_cb;
-};
-
-static const struct ble_gatt_dispatch_entry
-    ble_gatt_dispatch[BLE_GATT_OP_MAX] = {
-
-    [BLE_GATT_OP_MTU] = {
-        .kick_cb = ble_gatt_kick_mtu,
-        .err_cb = ble_gatt_err_mtu,
-    },
-    [BLE_GATT_OP_DISC_ALL_SERVICES] = {
-        .kick_cb = ble_gatt_kick_disc_all_services,
-        .err_cb = ble_gatt_err_disc_all_services,
-    },
-    [BLE_GATT_OP_DISC_SERVICE_UUID] = {
-        .kick_cb = ble_gatt_kick_disc_service_uuid,
-        .err_cb = ble_gatt_err_disc_service_uuid,
-    },
-    [BLE_GATT_OP_DISC_ALL_CHARS] = {
-        .kick_cb = ble_gatt_kick_disc_all_chars,
-        .err_cb = ble_gatt_err_disc_all_chars,
-    },
-    [BLE_GATT_OP_READ] = {
-        .kick_cb = ble_gatt_kick_read,
-        .err_cb = ble_gatt_err_read,
-    },
-    [BLE_GATT_OP_WRITE_NO_RSP] = {
-        .kick_cb = ble_gatt_kick_write_no_rsp,
-        .err_cb = NULL,
-    },
-    [BLE_GATT_OP_WRITE] = {
-        .kick_cb = ble_gatt_kick_write,
-        .err_cb = ble_gatt_err_write,
-    },
-};
-
-#define BLE_GATT_ENTRY_F_PENDING    0x01
-#define BLE_GATT_ENTRY_F_EXPECTING  0x02
-#define BLE_GATT_ENTRY_F_CONGESTED  0x04
-#define BLE_GATT_ENTRY_F_NO_MEM     0x08
-
-#define BLE_GATT_NUM_ENTRIES          4
-static void *ble_gatt_entry_mem;
-static struct os_mempool ble_gatt_entry_pool;
-
-static STAILQ_HEAD(, ble_gatt_entry) ble_gatt_list;
-
-/*****************************************************************************
- * @entry                                                                    *
- *****************************************************************************/
-
-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)
-{
-    struct ble_gatt_entry *entry;
-
-    entry = os_memblock_get(&ble_gatt_entry_pool);
-    if (entry != NULL) {
-        memset(entry, 0, sizeof *entry);
-    }
-
-    return entry;
-}
-
-static void
-ble_gatt_entry_free(struct ble_gatt_entry *entry)
-{
-    int rc;
-
-    rc = os_memblock_put(&ble_gatt_entry_pool, entry);
-    assert(rc == 0);
-}
-
-static void
-ble_gatt_entry_remove(struct ble_gatt_entry *entry,
-                      struct ble_gatt_entry *prev)
-{
-    if (prev == NULL) {
-        assert(STAILQ_FIRST(&ble_gatt_list) == entry);
-        STAILQ_REMOVE_HEAD(&ble_gatt_list, next);
-    } else {
-        assert(STAILQ_NEXT(prev, next) == entry);
-        STAILQ_NEXT(prev, next) = STAILQ_NEXT(entry, next);
-    }
-}
-
-static void
-ble_gatt_entry_remove_free(struct ble_gatt_entry *entry,
-                           struct ble_gatt_entry *prev)
-{
-    ble_gatt_entry_remove(entry, prev);
-    ble_gatt_entry_free(entry);
-}
-
-static int
-ble_gatt_entry_matches(struct ble_gatt_entry *entry, uint16_t conn_handle,
-                       uint8_t att_op, int expecting_only)
-{
-    if (conn_handle != entry->conn_handle) {
-        return 0;
-    }
-
-    if (att_op != entry->op && att_op != BLE_GATT_OP_NONE) {
-        return 0;
-    }
-
-    if (expecting_only &&
-        !(entry->flags & BLE_GATT_ENTRY_F_EXPECTING)) {
-
-        return 0;
-    }
-
-    return 1;
-}
-
-static struct ble_gatt_entry *
-ble_gatt_find(uint16_t conn_handle, uint8_t att_op, int expecting_only,
-              struct ble_gatt_entry **out_prev)
-{
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-
-    prev = NULL;
-    STAILQ_FOREACH(entry, &ble_gatt_list, next) {
-        if (ble_gatt_entry_matches(entry, conn_handle, att_op,
-                                   expecting_only)) {
-            if (out_prev != NULL) {
-                *out_prev = prev;
-            }
-            return entry;
-        }
-
-        prev = entry;
-    }
-
-    return NULL;
-}
-
-static void
-ble_gatt_entry_set_pending(struct ble_gatt_entry *entry)
-{
-    assert(!(entry->flags & BLE_GATT_ENTRY_F_PENDING));
-
-    entry->flags &= ~BLE_GATT_ENTRY_F_EXPECTING;
-    entry->flags |= BLE_GATT_ENTRY_F_PENDING;
-    ble_hs_kick_gatt();
-}
-
-static void
-ble_gatt_entry_set_expecting(struct ble_gatt_entry *entry,
-                             struct ble_gatt_entry *prev)
-{
-    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);
-}
-
-static int
-ble_gatt_new_entry(uint16_t conn_handle, uint8_t op,
-                   struct ble_gatt_entry **entry)
-{
-    struct ble_hs_conn *conn;
-
-    *entry = NULL;
-
-    /* Ensure we have a connection with the specified handle. */
-    conn = ble_hs_conn_find(conn_handle);
-    if (conn == NULL) {
-        return BLE_HS_ENOTCONN;
-    }
-
-    *entry = ble_gatt_entry_alloc();
-    if (*entry == NULL) {
-        return BLE_HS_ENOMEM;
-    }
-
-    memset(*entry, 0, sizeof **entry);
-    (*entry)->op = op;
-    (*entry)->conn_handle = conn_handle;
-
-    STAILQ_INSERT_TAIL(&ble_gatt_list, *entry, next);
-
-    ble_gatt_entry_set_pending(*entry);
-
-    return 0;
-}
-
-static int
-ble_gatt_entry_can_pend(struct ble_gatt_entry *entry)
-{
-    return !(entry->flags & (BLE_GATT_ENTRY_F_CONGESTED |
-                             BLE_GATT_ENTRY_F_NO_MEM |
-                             BLE_GATT_ENTRY_F_EXPECTING));
-}
-
-static void
-ble_gatt_retx_timer_ensure(void)
-{
-    int rc;
-
-    rc = os_callout_reset(&ble_gatt_retx_timer.cf_c,
-                          BLE_GATT_RETX_PERIOD * OS_TICKS_PER_SEC / 1000);
-    assert(rc == 0);
-}
-
-static void
-ble_gatt_retx_timer_exp(void *arg)
-{
-    struct ble_gatt_entry *entry;
-
-    STAILQ_FOREACH(entry, &ble_gatt_list, next) {
-        if (entry->flags & BLE_GATT_ENTRY_F_NO_MEM) {
-            entry->flags &= ~BLE_GATT_ENTRY_F_NO_MEM;
-            if (ble_gatt_entry_can_pend(entry)) {
-                ble_gatt_entry_set_pending(entry);
-            }
-        }
-    }
-}
-
-/**
- * @return                      1 if the transmit should be postponed; else 0.
- */
-static int
-ble_gatt_tx_postpone_chk(struct ble_gatt_entry *entry, int rc)
-{
-    switch (rc) {
-    case BLE_HS_ECONGESTED:
-        entry->flags |= BLE_GATT_ENTRY_F_CONGESTED;
-        return 1;
-
-    case BLE_HS_ENOMEM:
-        entry->flags |= BLE_GATT_ENTRY_F_NO_MEM;
-        ble_gatt_retx_timer_ensure();
-        return 1;
-
-    default:
-        return 0;
-    }
-}
-
-/*****************************************************************************
- * @mtu                                                                      *
- *****************************************************************************/
-
-static int
-ble_gatt_mtu_cb(struct ble_gatt_entry *entry, int status, uint16_t mtu)
-{
-    int rc;
-
-    if (entry->mtu.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->mtu.cb(entry->conn_handle, status, mtu, entry->mtu.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gatt_kick_mtu(struct ble_gatt_entry *entry)
-{
-    struct ble_att_mtu_cmd req;
-    struct ble_l2cap_chan *chan;
-    struct ble_hs_conn *conn;
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    chan = ble_hs_conn_chan_find(conn, BLE_L2CAP_CID_ATT);
-    assert(chan != NULL);
-
-    req.bamc_mtu = chan->blc_my_mtu;
-    rc = ble_att_clt_tx_mtu(conn, &req);
-    if (rc != 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_mtu_cb(entry, rc, 0);
-    return rc;
-}
-
-static void
-ble_gatt_err_mtu(struct ble_gatt_entry *entry, int status)
-{
-    ble_gatt_mtu_cb(entry, status, 0);
-}
-
-void
-ble_gatt_rx_mtu(struct ble_hs_conn *conn, uint16_t chan_mtu)
-{
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-
-    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_MTU, 1, &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    ble_gatt_mtu_cb(entry, 0, chan_mtu);
-    ble_gatt_entry_remove_free(entry, prev);
-}
-
-int
-ble_gatt_exchange_mtu(uint16_t conn_handle)
-{
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_MTU, &entry);
-    if (rc != 0) {
-        return rc;
-    }
-
-    return 0;
-}
-
-/*****************************************************************************
- * @discover all services                                                    *
- *****************************************************************************/
-
-static int
-ble_gatt_disc_all_services_cb(struct ble_gatt_entry *entry,
-                              int status, struct ble_gatt_service *service)
-{
-    int rc;
-
-    if (entry->disc_all_services.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->disc_all_services.cb(entry->conn_handle, status, service,
-                                         entry->disc_all_services.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gatt_kick_disc_all_services(struct ble_gatt_entry *entry)
-{
-    struct ble_att_read_group_type_req req;
-    struct ble_hs_conn *conn;
-    uint8_t uuid128[16];
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_PRIMARY_SERVICE, uuid128);
-    assert(rc == 0);
-
-    req.bagq_start_handle = entry->disc_all_services.prev_handle + 1;
-    req.bagq_end_handle = 0xffff;
-    rc = ble_att_clt_tx_read_group_type(conn, &req, uuid128);
-    if (rc != 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_disc_all_services_cb(entry, rc, NULL);
-    return rc;
-}
-
-static void
-ble_gatt_err_disc_all_services(struct ble_gatt_entry *entry, int status)
-{
-    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
-        /* Discovery is complete. */
-        status = 0;
-    }
-
-    ble_gatt_disc_all_services_cb(entry, status, NULL);
-}
-
-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_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 = BLE_HS_EMSGSIZE;
-        goto done;
-    }
-
-    entry->disc_all_services.prev_handle = adata->end_group_handle;
-
-    service.start_handle = adata->att_handle;
-    service.end_handle = adata->end_group_handle;
-
-    rc = 0;
-
-done:
-    cbrc = ble_gatt_disc_all_services_cb(entry, rc, &service);
-    if (rc != 0 || cbrc != 0) {
-        ble_gatt_entry_remove_free(entry, prev);
-    }
-}
-
-void
-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_DISC_ALL_SERVICES, 1,
-                          &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    if (rc != 0 || entry->disc_all_services.prev_handle == 0xffff) {
-        /* Error or all services discovered. */
-        ble_gatt_disc_all_services_cb(entry, rc, NULL);
-        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)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_DISC_ALL_SERVICES,
-                            &entry);
-    if (rc != 0) {
-        return rc;
-    }
-    entry->disc_all_services.prev_handle = 0x0000;
-    entry->disc_all_services.cb = cb;
-    entry->disc_all_services.cb_arg = cb_arg;
-
-    return 0;
-}
-
-/*****************************************************************************
- * @discover service by uuid                                                 *
- *****************************************************************************/
-
-static int
-ble_gatt_disc_service_uuid_cb(struct ble_gatt_entry *entry, int status,
-                              struct ble_gatt_service *service)
-{
-    int rc;
-
-    if (entry->disc_service_uuid.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->disc_service_uuid.cb(entry->conn_handle, status, service,
-                                         entry->disc_service_uuid.cb_arg);
-    }
-
-    return rc;
-}
-
-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) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    req.bavq_start_handle = entry->disc_service_uuid.prev_handle + 1;
-    req.bavq_end_handle = 0xffff;
-    req.bavq_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) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_disc_service_uuid_cb(entry, rc, NULL);
-    return rc;
-}
-
-static void
-ble_gatt_err_disc_service_uuid(struct ble_gatt_entry *entry, int status)
-{
-    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
-        /* Discovery is complete. */
-        status = 0;
-    }
-
-    ble_gatt_disc_service_uuid_cb(entry, status, NULL);
-}
-
-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;
-    }
-
-    entry->disc_service_uuid.prev_handle = adata->end_group_handle;
-
-    service.start_handle = adata->att_handle;
-    service.end_handle = adata->end_group_handle;
-    memcpy(service.uuid128, entry->disc_service_uuid.service_uuid, 16);
-
-    rc = ble_gatt_disc_service_uuid_cb(entry, 0, &service);
-    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. */
-        ble_gatt_disc_service_uuid_cb(entry, rc, NULL);
-        ble_gatt_entry_remove_free(entry, prev);
-    } else {
-        /* Send follow-up request. */
-        ble_gatt_entry_set_pending(entry);
-    }
+    return ble_gattc_disc_all_services(conn_handle, cb, 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)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_DISC_SERVICE_UUID,
-                            &entry);
-    if (rc != 0) {
-        return rc;
-    }
-    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;
-}
-
-/*****************************************************************************
- * @discover all characteristics                                             *
- *****************************************************************************/
-
-static int
-ble_gatt_disc_all_chars_cb(struct ble_gatt_entry *entry, int status,
-                           struct ble_gatt_chr *chr)
-{
-    int rc;
-
-    if (entry->disc_all_chars.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->disc_all_chars.cb(entry->conn_handle, status, chr,
-                                      entry->disc_all_chars.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gatt_kick_disc_all_chars(struct ble_gatt_entry *entry)
-{
-    struct ble_att_read_type_req req;
-    struct ble_hs_conn *conn;
-    uint8_t uuid128[16];
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
-    assert(rc == 0);
-
-    req.batq_start_handle = entry->disc_all_chars.prev_handle + 1;
-    req.batq_end_handle = entry->disc_all_chars.end_handle;
-
-    rc = ble_att_clt_tx_read_type(conn, &req, uuid128);
-    if (rc != 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_disc_all_chars_cb(entry, rc, NULL);
-    return rc;
-}
-
-static void
-ble_gatt_err_disc_all_chars(struct ble_gatt_entry *entry, int status)
-{
-    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
-        /* Discovery is complete. */
-        status = 0;
-    }
-
-    ble_gatt_disc_all_chars_cb(entry, status, NULL);
-}
-
-void
-ble_gatt_rx_read_type_adata(struct ble_hs_conn *conn,
-                            struct ble_att_clt_adata *adata)
-{
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-    struct ble_gatt_chr chr;
-    uint16_t uuid16;
-    int cbrc;
-    int rc;
-
-    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_CHARS, 1,
-                          &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    memset(&chr, 0, sizeof chr);
-    chr.decl_handle = adata->att_handle;
-
-    switch (adata->value_len) {
-    case BLE_GATT_CHR_DECL_SZ_16:
-        uuid16 = le16toh(adata->value + 3);
-        rc = ble_hs_uuid_from_16bit(uuid16, chr.uuid128);
-        if (rc != 0) {
-            rc = BLE_HS_EBADDATA;
-            goto done;
-        }
-        break;
-
-    case BLE_GATT_CHR_DECL_SZ_128:
-        memcpy(chr.uuid128, adata->value + 3, 16);
-        break;
-
-    default:
-        rc = BLE_HS_EBADDATA;
-        goto done;
-    }
-
-    entry->disc_all_chars.prev_handle = adata->att_handle;
-
-    chr.properties = adata->value[0];
-    chr.value_handle = le16toh(adata->value + 1);
-
-    rc = 0;
-
-done:
-    cbrc = ble_gatt_disc_all_chars_cb(entry, rc, &chr);
-    if (rc != 0 || cbrc != 0) {
-        ble_gatt_entry_remove_free(entry, prev);
-    }
-}
-
-void
-ble_gatt_rx_read_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_DISC_ALL_CHARS, 1,
-                          &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    if (rc != 0 || entry->disc_all_chars.prev_handle ==
-                   entry->disc_all_chars.end_handle) {
-        /* Error or all services discovered. */
-        ble_gatt_disc_all_chars_cb(entry, rc, NULL);
-        ble_gatt_entry_remove_free(entry, prev);
-    } else {
-        /* Send follow-up request. */
-        ble_gatt_entry_set_pending(entry);
-    }
+    return ble_gattc_disc_service_by_uuid(conn_handle, service_uuid128, cb,
+                                          cb_arg);
 }
 
 int
@@ -896,389 +20,45 @@ ble_gatt_disc_all_chars(uint16_t conn_handle, uint16_t start_handle,
                         uint16_t end_handle, ble_gatt_chr_fn *cb,
                         void *cb_arg)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_DISC_ALL_CHARS, &entry);
-    if (rc != 0) {
-        return rc;
-    }
-    entry->disc_all_chars.prev_handle = start_handle - 1;
-    entry->disc_all_chars.end_handle = end_handle;
-    entry->disc_all_chars.cb = cb;
-    entry->disc_all_chars.cb_arg = cb_arg;
-
-    return 0;
-}
-
-/*****************************************************************************
- * @read                                                                     *
- *****************************************************************************/
-
-static int
-ble_gatt_read_cb(struct ble_gatt_entry *entry, int status,
-                 struct ble_gatt_attr *attr)
-{
-    int rc;
-
-    if (entry->read.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->read.cb(entry->conn_handle, status, attr,
-                            entry->read.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gatt_kick_read(struct ble_gatt_entry *entry)
-{
-    struct ble_att_read_req req;
-    struct ble_hs_conn *conn;
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    req.barq_handle = entry->read.handle;
-    rc = ble_att_clt_tx_read(conn, &req);
-    if (rc != 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_read_cb(entry, rc, NULL);
-    return rc;
-}
-
-static void
-ble_gatt_err_read(struct ble_gatt_entry *entry, int status)
-{
-    ble_gatt_read_cb(entry, status, NULL);
-}
-
-void
-ble_gatt_rx_read_rsp(struct ble_hs_conn *conn, int status, void *value,
-                     int value_len)
-{
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-    struct ble_gatt_attr attr;
-
-    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_READ, 1, &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    attr.handle = entry->read.handle;
-    attr.value_len = value_len;
-    attr.value = value;
-
-    ble_gatt_read_cb(entry, status, &attr);
-
-    /* The read operation only has a single request / response exchange. */
-    ble_gatt_entry_remove_free(entry, prev);
+    return ble_gattc_disc_all_chars(conn_handle, start_handle, end_handle, cb,
+                                    cb_arg);
 }
 
 int
 ble_gatt_read(uint16_t conn_handle, uint16_t attr_handle,
               ble_gatt_attr_fn *cb, void *cb_arg)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_READ, &entry);
-    if (rc != 0) {
-        return rc;
-    }
-    entry->read.handle = attr_handle;
-    entry->read.cb = cb;
-    entry->read.cb_arg = cb_arg;
-
-    return 0;
-}
-
-/*****************************************************************************
- * @write no response                                                        *
- *****************************************************************************/
-
-static int
-ble_gatt_write_cb(struct ble_gatt_entry *entry, int status)
-{
-    int rc;
-
-    if (entry->write.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->write.cb(entry->conn_handle, status, &entry->write.attr,
-                             entry->write.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gatt_kick_write_no_rsp(struct ble_gatt_entry *entry)
-{
-    struct ble_att_write_req req;
-    struct ble_hs_conn *conn;
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    req.bawq_handle = entry->write.attr.handle;
-    rc = ble_att_clt_tx_write_cmd(conn, &req, entry->write.attr.value,
-                                  entry->write.attr.value_len);
-    if (rc != 0) {
-        goto err;
-    }
-
-    /* No response expected; call callback immediately and return nonzero to
-     * indicate the entry should be freed.
-     */
-    ble_gatt_write_cb(entry, 0);
-
-    return 1;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_write_cb(entry, rc);
-    return rc;
+    return ble_gattc_read(conn_handle, attr_handle, cb, cb_arg);
 }
 
 int
-ble_gatt_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, void *value,
-                      uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg)
+ble_gatt_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
+                      void *value, uint16_t value_len,
+                      ble_gatt_attr_fn *cb, void *cb_arg)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_WRITE_NO_RSP, &entry);
-    if (rc != 0) {
-        return rc;
-    }
-
-    entry->write.attr.handle = attr_handle;
-    entry->write.attr.value = value;
-    entry->write.attr.value_len = value_len;
-    entry->write.cb = cb;
-    entry->write.cb_arg = cb_arg;
-
-    return 0;
-}
-
-/*****************************************************************************
- * @write                                                                    *
- *****************************************************************************/
-
-static int
-ble_gatt_kick_write(struct ble_gatt_entry *entry)
-{
-    struct ble_att_write_req req;
-    struct ble_hs_conn *conn;
-    int rc;
-
-    conn = ble_hs_conn_find(entry->conn_handle);
-    if (conn == NULL) {
-        rc = BLE_HS_ENOTCONN;
-        goto err;
-    }
-
-    req.bawq_handle = entry->write.attr.handle;
-    rc = ble_att_clt_tx_write_req(conn, &req, entry->write.attr.value,
-                                  entry->write.attr.value_len);
-    if (rc != 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    if (ble_gatt_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
-    }
-
-    ble_gatt_write_cb(entry, rc);
-    return rc;
-}
-
-static void
-ble_gatt_err_write(struct ble_gatt_entry *entry, int status)
-{
-    ble_gatt_write_cb(entry, status);
-}
-
-void
-ble_gatt_rx_write_rsp(struct ble_hs_conn *conn)
-{
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-
-    entry = ble_gatt_find(conn->bhc_handle, BLE_GATT_OP_WRITE, 1, &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    ble_gatt_write_cb(entry, 0);
-
-    /* The write operation only has a single request / response exchange. */
-    ble_gatt_entry_remove_free(entry, prev);
+    return ble_gattc_write_no_rsp(conn_handle, attr_handle, value, value_len,
+                                  cb, cb_arg);
 }
 
 int
 ble_gatt_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
                uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg)
 {
-    struct ble_gatt_entry *entry;
-    int rc;
-
-    rc = ble_gatt_new_entry(conn_handle, BLE_GATT_OP_WRITE, &entry);
-    if (rc != 0) {
-        return rc;
-    }
-
-    entry->write.attr.handle = attr_handle;
-    entry->write.attr.value = value;
-    entry->write.attr.value_len = value_len;
-    entry->write.cb = cb;
-    entry->write.cb_arg = cb_arg;
-
-    return 0;
+    return ble_gattc_write(conn_handle, attr_handle, value, value_len, cb,
+                           cb_arg);
 }
 
-/*****************************************************************************
- * @misc                                                                     *
- *****************************************************************************/
-
-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;
-    int rc;
-
-    prev = NULL;
-    entry = STAILQ_FIRST(&ble_gatt_list);
-    while (entry != NULL) {
-        next = STAILQ_NEXT(entry, next);
-
-        if (entry->flags & BLE_GATT_ENTRY_F_PENDING) {
-            dispatch = ble_gatt_dispatch_get(entry->op);
-            rc = dispatch->kick_cb(entry);
-            switch (rc) {
-            case 0:
-                /* Transmit succeeded.  Response expected. */
-                ble_gatt_entry_set_expecting(entry, prev);
-                /* Current entry got moved to back; old prev still valid. */
-                break;
-
-            case BLE_HS_EAGAIN:
-                /* Transmit failed due to resource shortage.  Reschedule. */
-                entry->flags &= ~BLE_GATT_ENTRY_F_PENDING;
-                /* Current entry remains; reseat prev. */
-                prev = entry;
-                break;
-
-            default:
-                /* Transmit failed.  Abort procedure. */
-                ble_gatt_entry_remove_free(entry, prev);
-                /* Current entry removed; old prev still valid. */
-                break;
-            }
-        } else {
-            prev = entry;
-        }
-
-        entry = next;
-    }
-}
-
-void
-ble_gatt_rx_err(uint16_t conn_handle, struct ble_att_error_rsp *rsp)
+int
+ble_gatt_exchange_mtu(uint16_t conn_handle)
 {
-    const struct ble_gatt_dispatch_entry *dispatch;
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-
-    entry = ble_gatt_find(conn_handle, BLE_GATT_OP_NONE, 1, &prev);
-    if (entry == NULL) {
-        /* Not expecting a response from this device. */
-        return;
-    }
-
-    dispatch = ble_gatt_dispatch_get(entry->op);
-    if (dispatch->err_cb != NULL) {
-        dispatch->err_cb(entry, BLE_HS_ERR_ATT_BASE + rsp->baep_error_code);
-    }
-
-    ble_gatt_entry_remove_free(entry, prev);
+    return ble_gattc_exchange_mtu(conn_handle);
 }
 
-/**
- * Called when a BLE connection ends.  Frees all GATT resources associated with
- * the connection and cancels all relevant pending and in-progress GATT
- * procedures.
- */
 void
 ble_gatt_connection_broken(uint16_t conn_handle)
 {
-    const struct ble_gatt_dispatch_entry *dispatch;
-    struct ble_gatt_entry *entry;
-    struct ble_gatt_entry *prev;
-
-    while (1) {
-        entry = ble_gatt_find(conn_handle, BLE_GATT_OP_NONE, 0, &prev);
-        if (entry == NULL) {
-            break;
-        }
-
-        dispatch = ble_gatt_dispatch_get(entry->op);
-        dispatch->err_cb(entry, BLE_HS_ENOTCONN);
-
-        ble_gatt_entry_remove_free(entry, prev);
-    }
-}
-
-/**
- * Called when a BLE connection transitions into a transmittable state.  Wakes
- * up all congested GATT procedures associated with the connection.
- */
-void
-ble_gatt_connection_txable(uint16_t conn_handle)
-{
-    struct ble_gatt_entry *entry;
-
-    STAILQ_FOREACH(entry, &ble_gatt_list, next) {
-        if (entry->conn_handle == conn_handle &&
-            entry->flags & BLE_GATT_ENTRY_F_CONGESTED) {
-
-            entry->flags &= ~BLE_GATT_ENTRY_F_CONGESTED;
-            if (ble_gatt_entry_can_pend(entry)) {
-                ble_gatt_entry_set_pending(entry);
-            }
-        }
-    }
+    ble_gattc_connection_broken(conn_handle);
+    /* XXX: Notify GATT server. */
 }
 
 int
@@ -1286,35 +66,12 @@ ble_gatt_init(void)
 {
     int rc;
 
-    free(ble_gatt_entry_mem);
-
-    ble_gatt_entry_mem = malloc(
-        OS_MEMPOOL_BYTES(BLE_GATT_NUM_ENTRIES,
-                         sizeof (struct ble_gatt_entry)));
-    if (ble_gatt_entry_mem == NULL) {
-        rc = BLE_HS_ENOMEM;
-        goto err;
-    }
-
-    rc = os_mempool_init(&ble_gatt_entry_pool,
-                         BLE_GATT_NUM_ENTRIES,
-                         sizeof (struct ble_gatt_entry),
-                         ble_gatt_entry_mem,
-                         "ble_gatt_entry_pool");
+    rc = ble_gattc_init();
     if (rc != 0) {
-        goto err;
+        return rc;
     }
 
-    STAILQ_INIT(&ble_gatt_list);
-
-    os_callout_func_init(&ble_gatt_retx_timer, &ble_hs_evq,
-                         ble_gatt_retx_timer_exp, NULL);
+    /* XXX: Init server. */
 
     return 0;
-
-err:
-    free(ble_gatt_entry_mem);
-    ble_gatt_entry_mem = NULL;
-
-    return rc;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/ble_gatt_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt_priv.h b/net/nimble/host/src/ble_gatt_priv.h
index 6eb1dfd..8a7e850 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -22,22 +22,46 @@
 #define BLE_GATT_CHR_DECL_SZ_16     5
 #define BLE_GATT_CHR_DECL_SZ_128    19
 
-void ble_gatt_rx_err(uint16_t conn_handle, 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);
-void ble_gatt_rx_read_type_adata(struct ble_hs_conn *conn,
-                                 struct ble_att_clt_adata *adata);
-void ble_gatt_rx_read_type_complete(struct ble_hs_conn *conn, int rc);
-void ble_gatt_rx_read_rsp(struct ble_hs_conn *conn, int status, void *value,
-                          int value_len);
-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);
-void ble_gatt_rx_write_rsp(struct ble_hs_conn *conn);
-void ble_gatt_connection_txable(uint16_t conn_handle);
+/*** @gen */
 void ble_gatt_connection_broken(uint16_t conn_handle);
 
+/*** @client */
+int ble_gattc_disc_all_services(uint16_t conn_handle,
+                               ble_gatt_disc_service_fn *cb,
+                               void *cb_arg);
+int ble_gattc_disc_service_by_uuid(uint16_t conn_handle, void *service_uuid128,
+                                  ble_gatt_disc_service_fn *cb, void *cb_arg);
+int ble_gattc_disc_all_chars(uint16_t conn_handle, uint16_t start_handle,
+                            uint16_t end_handle, ble_gatt_chr_fn *cb,
+                            void *cb_arg);
+int ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle,
+                  ble_gatt_attr_fn *cb, void *cb_arg);
+int ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
+                          void *value, uint16_t value_len,
+                          ble_gatt_attr_fn *cb, void *cb_arg);
+int ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
+                   uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg);
+
+int ble_gattc_exchange_mtu(uint16_t conn_handle);
+
+void ble_gattc_rx_err(uint16_t conn_handle, struct ble_att_error_rsp *rsp);
+void ble_gattc_wakeup(void);
+void ble_gattc_rx_mtu(struct ble_hs_conn *conn, uint16_t chan_mtu);
+void ble_gattc_rx_read_type_adata(struct ble_hs_conn *conn,
+                                  struct ble_att_clt_adata *adata);
+void ble_gattc_rx_read_type_complete(struct ble_hs_conn *conn, int rc);
+void ble_gattc_rx_read_rsp(struct ble_hs_conn *conn, int status, void *value,
+                           int value_len);
+void ble_gattc_rx_read_group_type_adata(struct ble_hs_conn *conn,
+                                        struct ble_att_clt_adata *adata);
+void ble_gattc_rx_read_group_type_complete(struct ble_hs_conn *conn, int rc);
+void ble_gattc_rx_find_type_value_hinfo(struct ble_hs_conn *conn,
+                                        struct ble_att_clt_adata *adata);
+void ble_gattc_rx_find_type_value_complete(struct ble_hs_conn *conn, int rc);
+void ble_gattc_rx_write_rsp(struct ble_hs_conn *conn);
+void ble_gattc_connection_txable(uint16_t conn_handle);
+void ble_gattc_connection_broken(uint16_t conn_handle);
+
+int ble_gattc_init(void);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/ble_gattc.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gattc.c b/net/nimble/host/src/ble_gattc.c
new file mode 100644
index 0000000..bd48e41
--- /dev/null
+++ b/net/nimble/host/src/ble_gattc.c
@@ -0,0 +1,1320 @@
+/**
+ * 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 <stddef.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "os/os_mempool.h"
+#include "nimble/ble.h"
+#include "ble_gatt_priv.h"
+#include "ble_hs_priv.h"
+#include "host/ble_hs_uuid.h"
+#include "ble_hs_conn.h"
+#include "ble_att_cmd.h"
+#include "ble_att_priv.h"
+
+struct ble_gattc_entry {
+    STAILQ_ENTRY(ble_gattc_entry) next;
+
+    uint8_t op;
+    uint8_t flags;
+    uint16_t conn_handle;
+    union {
+        struct {
+            int (*cb)(int status, uint16_t conn_handle, uint16_t mtu,
+                      void *arg);
+            void *cb_arg;
+        } mtu;
+
+        struct {
+            uint16_t prev_handle;
+            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;
+
+        struct {
+            uint16_t prev_handle;
+            uint16_t end_handle;
+            ble_gatt_chr_fn *cb;
+            void *cb_arg;
+        } disc_all_chars;
+
+        struct {
+            uint16_t handle;
+            ble_gatt_attr_fn *cb;
+            void *cb_arg;
+        } read;
+
+        struct {
+            struct ble_gatt_attr attr;
+            ble_gatt_attr_fn *cb;
+            void *cb_arg;
+        } write;
+    };
+};
+
+#define BLE_GATT_RETX_PERIOD                    1000 /* Milliseconds. */
+
+#define BLE_GATT_OP_NONE                        UINT8_MAX
+#define BLE_GATT_OP_MTU                         0
+#define BLE_GATT_OP_DISC_ALL_SERVICES           1
+#define BLE_GATT_OP_DISC_SERVICE_UUID           2
+#define BLE_GATT_OP_DISC_ALL_CHARS              3
+#define BLE_GATT_OP_READ                        4
+#define BLE_GATT_OP_WRITE_NO_RSP                5
+#define BLE_GATT_OP_WRITE                       6
+#define BLE_GATT_OP_MAX                         7
+
+static struct os_callout_func ble_gattc_retx_timer;
+
+typedef int ble_gattc_kick_fn(struct ble_gattc_entry *entry);
+typedef void ble_gattc_err_fn(struct ble_gattc_entry *entry, int status);
+
+static int ble_gattc_kick_mtu(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_disc_all_services(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_disc_service_uuid(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_disc_all_chars(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_read(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_write_no_rsp(struct ble_gattc_entry *entry);
+static int ble_gattc_kick_write(struct ble_gattc_entry *entry);
+
+static void ble_gattc_err_mtu(struct ble_gattc_entry *entry, int status);
+static void ble_gattc_err_disc_all_services(struct ble_gattc_entry *entry,
+                                           int status);
+static void ble_gattc_err_disc_service_uuid(struct ble_gattc_entry *entry,
+                                           int status);
+static void ble_gattc_err_disc_all_chars(struct ble_gattc_entry *entry,
+                                        int status);
+static void ble_gattc_err_read(struct ble_gattc_entry *entry, int status);
+static void ble_gattc_err_write(struct ble_gattc_entry *entry, int status);
+
+struct ble_gattc_dispatch_entry {
+    ble_gattc_kick_fn *kick_cb;
+    ble_gattc_err_fn *err_cb;
+};
+
+static const struct ble_gattc_dispatch_entry
+    ble_gattc_dispatch[BLE_GATT_OP_MAX] = {
+
+    [BLE_GATT_OP_MTU] = {
+        .kick_cb = ble_gattc_kick_mtu,
+        .err_cb = ble_gattc_err_mtu,
+    },
+    [BLE_GATT_OP_DISC_ALL_SERVICES] = {
+        .kick_cb = ble_gattc_kick_disc_all_services,
+        .err_cb = ble_gattc_err_disc_all_services,
+    },
+    [BLE_GATT_OP_DISC_SERVICE_UUID] = {
+        .kick_cb = ble_gattc_kick_disc_service_uuid,
+        .err_cb = ble_gattc_err_disc_service_uuid,
+    },
+    [BLE_GATT_OP_DISC_ALL_CHARS] = {
+        .kick_cb = ble_gattc_kick_disc_all_chars,
+        .err_cb = ble_gattc_err_disc_all_chars,
+    },
+    [BLE_GATT_OP_READ] = {
+        .kick_cb = ble_gattc_kick_read,
+        .err_cb = ble_gattc_err_read,
+    },
+    [BLE_GATT_OP_WRITE_NO_RSP] = {
+        .kick_cb = ble_gattc_kick_write_no_rsp,
+        .err_cb = NULL,
+    },
+    [BLE_GATT_OP_WRITE] = {
+        .kick_cb = ble_gattc_kick_write,
+        .err_cb = ble_gattc_err_write,
+    },
+};
+
+#define BLE_GATT_ENTRY_F_PENDING    0x01
+#define BLE_GATT_ENTRY_F_EXPECTING  0x02
+#define BLE_GATT_ENTRY_F_CONGESTED  0x04
+#define BLE_GATT_ENTRY_F_NO_MEM     0x08
+
+#define BLE_GATT_NUM_ENTRIES          4
+static void *ble_gattc_entry_mem;
+static struct os_mempool ble_gattc_entry_pool;
+
+static STAILQ_HEAD(, ble_gattc_entry) ble_gattc_list;
+
+/*****************************************************************************
+ * @entry                                                                    *
+ *****************************************************************************/
+
+static const struct ble_gattc_dispatch_entry *
+ble_gattc_dispatch_get(uint8_t op)
+{
+    assert(op < BLE_GATT_OP_MAX);
+    return ble_gattc_dispatch + op;
+}
+
+static struct ble_gattc_entry *
+ble_gattc_entry_alloc(void)
+{
+    struct ble_gattc_entry *entry;
+
+    entry = os_memblock_get(&ble_gattc_entry_pool);
+    if (entry != NULL) {
+        memset(entry, 0, sizeof *entry);
+    }
+
+    return entry;
+}
+
+static void
+ble_gattc_entry_free(struct ble_gattc_entry *entry)
+{
+    int rc;
+
+    rc = os_memblock_put(&ble_gattc_entry_pool, entry);
+    assert(rc == 0);
+}
+
+static void
+ble_gattc_entry_remove(struct ble_gattc_entry *entry,
+                      struct ble_gattc_entry *prev)
+{
+    if (prev == NULL) {
+        assert(STAILQ_FIRST(&ble_gattc_list) == entry);
+        STAILQ_REMOVE_HEAD(&ble_gattc_list, next);
+    } else {
+        assert(STAILQ_NEXT(prev, next) == entry);
+        STAILQ_NEXT(prev, next) = STAILQ_NEXT(entry, next);
+    }
+}
+
+static void
+ble_gattc_entry_remove_free(struct ble_gattc_entry *entry,
+                           struct ble_gattc_entry *prev)
+{
+    ble_gattc_entry_remove(entry, prev);
+    ble_gattc_entry_free(entry);
+}
+
+static int
+ble_gattc_entry_matches(struct ble_gattc_entry *entry, uint16_t conn_handle,
+                       uint8_t att_op, int expecting_only)
+{
+    if (conn_handle != entry->conn_handle) {
+        return 0;
+    }
+
+    if (att_op != entry->op && att_op != BLE_GATT_OP_NONE) {
+        return 0;
+    }
+
+    if (expecting_only &&
+        !(entry->flags & BLE_GATT_ENTRY_F_EXPECTING)) {
+
+        return 0;
+    }
+
+    return 1;
+}
+
+static struct ble_gattc_entry *
+ble_gattc_find(uint16_t conn_handle, uint8_t att_op, int expecting_only,
+              struct ble_gattc_entry **out_prev)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    prev = NULL;
+    STAILQ_FOREACH(entry, &ble_gattc_list, next) {
+        if (ble_gattc_entry_matches(entry, conn_handle, att_op,
+                                   expecting_only)) {
+            if (out_prev != NULL) {
+                *out_prev = prev;
+            }
+            return entry;
+        }
+
+        prev = entry;
+    }
+
+    return NULL;
+}
+
+static void
+ble_gattc_entry_set_pending(struct ble_gattc_entry *entry)
+{
+    assert(!(entry->flags & BLE_GATT_ENTRY_F_PENDING));
+
+    entry->flags &= ~BLE_GATT_ENTRY_F_EXPECTING;
+    entry->flags |= BLE_GATT_ENTRY_F_PENDING;
+    ble_hs_kick_gatt();
+}
+
+static void
+ble_gattc_entry_set_expecting(struct ble_gattc_entry *entry,
+                             struct ble_gattc_entry *prev)
+{
+    assert(!(entry->flags & BLE_GATT_ENTRY_F_EXPECTING));
+
+    ble_gattc_entry_remove(entry, prev);
+    entry->flags &= ~BLE_GATT_ENTRY_F_PENDING;
+    entry->flags |= BLE_GATT_ENTRY_F_EXPECTING;
+    STAILQ_INSERT_TAIL(&ble_gattc_list, entry, next);
+}
+
+static int
+ble_gattc_new_entry(uint16_t conn_handle, uint8_t op,
+                   struct ble_gattc_entry **entry)
+{
+    struct ble_hs_conn *conn;
+
+    *entry = NULL;
+
+    /* Ensure we have a connection with the specified handle. */
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn == NULL) {
+        return BLE_HS_ENOTCONN;
+    }
+
+    *entry = ble_gattc_entry_alloc();
+    if (*entry == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    memset(*entry, 0, sizeof **entry);
+    (*entry)->op = op;
+    (*entry)->conn_handle = conn_handle;
+
+    STAILQ_INSERT_TAIL(&ble_gattc_list, *entry, next);
+
+    ble_gattc_entry_set_pending(*entry);
+
+    return 0;
+}
+
+static int
+ble_gattc_entry_can_pend(struct ble_gattc_entry *entry)
+{
+    return !(entry->flags & (BLE_GATT_ENTRY_F_CONGESTED |
+                             BLE_GATT_ENTRY_F_NO_MEM |
+                             BLE_GATT_ENTRY_F_EXPECTING));
+}
+
+static void
+ble_gattc_retx_timer_ensure(void)
+{
+    int rc;
+
+    rc = os_callout_reset(&ble_gattc_retx_timer.cf_c,
+                          BLE_GATT_RETX_PERIOD * OS_TICKS_PER_SEC / 1000);
+    assert(rc == 0);
+}
+
+static void
+ble_gattc_retx_timer_exp(void *arg)
+{
+    struct ble_gattc_entry *entry;
+
+    STAILQ_FOREACH(entry, &ble_gattc_list, next) {
+        if (entry->flags & BLE_GATT_ENTRY_F_NO_MEM) {
+            entry->flags &= ~BLE_GATT_ENTRY_F_NO_MEM;
+            if (ble_gattc_entry_can_pend(entry)) {
+                ble_gattc_entry_set_pending(entry);
+            }
+        }
+    }
+}
+
+/**
+ * @return                      1 if the transmit should be postponed; else 0.
+ */
+static int
+ble_gattc_tx_postpone_chk(struct ble_gattc_entry *entry, int rc)
+{
+    switch (rc) {
+    case BLE_HS_ECONGESTED:
+        entry->flags |= BLE_GATT_ENTRY_F_CONGESTED;
+        return 1;
+
+    case BLE_HS_ENOMEM:
+        entry->flags |= BLE_GATT_ENTRY_F_NO_MEM;
+        ble_gattc_retx_timer_ensure();
+        return 1;
+
+    default:
+        return 0;
+    }
+}
+
+/*****************************************************************************
+ * @mtu                                                                      *
+ *****************************************************************************/
+
+static int
+ble_gattc_mtu_cb(struct ble_gattc_entry *entry, int status, uint16_t mtu)
+{
+    int rc;
+
+    if (entry->mtu.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->mtu.cb(entry->conn_handle, status, mtu, entry->mtu.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_mtu(struct ble_gattc_entry *entry)
+{
+    struct ble_att_mtu_cmd req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    chan = ble_hs_conn_chan_find(conn, BLE_L2CAP_CID_ATT);
+    assert(chan != NULL);
+
+    req.bamc_mtu = chan->blc_my_mtu;
+    rc = ble_att_clt_tx_mtu(conn, &req);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_mtu_cb(entry, rc, 0);
+    return rc;
+}
+
+static void
+ble_gattc_err_mtu(struct ble_gattc_entry *entry, int status)
+{
+    ble_gattc_mtu_cb(entry, status, 0);
+}
+
+void
+ble_gattc_rx_mtu(struct ble_hs_conn *conn, uint16_t chan_mtu)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_MTU, 1, &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    ble_gattc_mtu_cb(entry, 0, chan_mtu);
+    ble_gattc_entry_remove_free(entry, prev);
+}
+
+int
+ble_gattc_exchange_mtu(uint16_t conn_handle)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_MTU, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @discover all services                                                    *
+ *****************************************************************************/
+
+static int
+ble_gattc_disc_all_services_cb(struct ble_gattc_entry *entry,
+                              int status, struct ble_gatt_service *service)
+{
+    int rc;
+
+    if (entry->disc_all_services.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->disc_all_services.cb(entry->conn_handle, status, service,
+                                         entry->disc_all_services.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_disc_all_services(struct ble_gattc_entry *entry)
+{
+    struct ble_att_read_group_type_req req;
+    struct ble_hs_conn *conn;
+    uint8_t uuid128[16];
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_PRIMARY_SERVICE, uuid128);
+    assert(rc == 0);
+
+    req.bagq_start_handle = entry->disc_all_services.prev_handle + 1;
+    req.bagq_end_handle = 0xffff;
+    rc = ble_att_clt_tx_read_group_type(conn, &req, uuid128);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_disc_all_services_cb(entry, rc, NULL);
+    return rc;
+}
+
+static void
+ble_gattc_err_disc_all_services(struct ble_gattc_entry *entry, int status)
+{
+    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
+        /* Discovery is complete. */
+        status = 0;
+    }
+
+    ble_gattc_disc_all_services_cb(entry, status, NULL);
+}
+
+void
+ble_gattc_rx_read_group_type_adata(struct ble_hs_conn *conn,
+                                  struct ble_att_clt_adata *adata)
+{
+    struct ble_gatt_service service;
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+    uint16_t uuid16;
+    int cbrc;
+    int rc;
+
+    entry = ble_gattc_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 = BLE_HS_EMSGSIZE;
+        goto done;
+    }
+
+    entry->disc_all_services.prev_handle = adata->end_group_handle;
+
+    service.start_handle = adata->att_handle;
+    service.end_handle = adata->end_group_handle;
+
+    rc = 0;
+
+done:
+    cbrc = ble_gattc_disc_all_services_cb(entry, rc, &service);
+    if (rc != 0 || cbrc != 0) {
+        ble_gattc_entry_remove_free(entry, prev);
+    }
+}
+
+void
+ble_gattc_rx_read_group_type_complete(struct ble_hs_conn *conn, int rc)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_SERVICES, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    if (rc != 0 || entry->disc_all_services.prev_handle == 0xffff) {
+        /* Error or all services discovered. */
+        ble_gattc_disc_all_services_cb(entry, rc, NULL);
+        ble_gattc_entry_remove_free(entry, prev);
+    } else {
+        /* Send follow-up request. */
+        ble_gattc_entry_set_pending(entry);
+    }
+}
+
+int
+ble_gattc_disc_all_services(uint16_t conn_handle, ble_gatt_disc_service_fn *cb,
+                            void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_DISC_ALL_SERVICES,
+                            &entry);
+    if (rc != 0) {
+        return rc;
+    }
+    entry->disc_all_services.prev_handle = 0x0000;
+    entry->disc_all_services.cb = cb;
+    entry->disc_all_services.cb_arg = cb_arg;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @discover service by uuid                                                 *
+ *****************************************************************************/
+
+static int
+ble_gattc_disc_service_uuid_cb(struct ble_gattc_entry *entry, int status,
+                              struct ble_gatt_service *service)
+{
+    int rc;
+
+    if (entry->disc_service_uuid.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->disc_service_uuid.cb(entry->conn_handle, status, service,
+                                         entry->disc_service_uuid.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_disc_service_uuid(struct ble_gattc_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) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    req.bavq_start_handle = entry->disc_service_uuid.prev_handle + 1;
+    req.bavq_end_handle = 0xffff;
+    req.bavq_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) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_disc_service_uuid_cb(entry, rc, NULL);
+    return rc;
+}
+
+static void
+ble_gattc_err_disc_service_uuid(struct ble_gattc_entry *entry, int status)
+{
+    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
+        /* Discovery is complete. */
+        status = 0;
+    }
+
+    ble_gattc_disc_service_uuid_cb(entry, status, NULL);
+}
+
+void
+ble_gattc_rx_find_type_value_hinfo(struct ble_hs_conn *conn,
+                                  struct ble_att_clt_adata *adata)
+{
+    struct ble_gatt_service service;
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+    int rc;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_DISC_SERVICE_UUID, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    entry->disc_service_uuid.prev_handle = adata->end_group_handle;
+
+    service.start_handle = adata->att_handle;
+    service.end_handle = adata->end_group_handle;
+    memcpy(service.uuid128, entry->disc_service_uuid.service_uuid, 16);
+
+    rc = ble_gattc_disc_service_uuid_cb(entry, 0, &service);
+    if (rc != 0) {
+        ble_gattc_entry_remove_free(entry, prev);
+    }
+}
+
+void
+ble_gattc_rx_find_type_value_complete(struct ble_hs_conn *conn, int rc)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_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. */
+        ble_gattc_disc_service_uuid_cb(entry, rc, NULL);
+        ble_gattc_entry_remove_free(entry, prev);
+    } else {
+        /* Send follow-up request. */
+        ble_gattc_entry_set_pending(entry);
+    }
+}
+
+int
+ble_gattc_disc_service_by_uuid(uint16_t conn_handle, void *service_uuid128,
+                               ble_gatt_disc_service_fn *cb, void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_DISC_SERVICE_UUID,
+                            &entry);
+    if (rc != 0) {
+        return rc;
+    }
+    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;
+}
+
+/*****************************************************************************
+ * @discover all characteristics                                             *
+ *****************************************************************************/
+
+static int
+ble_gattc_disc_all_chars_cb(struct ble_gattc_entry *entry, int status,
+                           struct ble_gatt_chr *chr)
+{
+    int rc;
+
+    if (entry->disc_all_chars.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->disc_all_chars.cb(entry->conn_handle, status, chr,
+                                      entry->disc_all_chars.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_disc_all_chars(struct ble_gattc_entry *entry)
+{
+    struct ble_att_read_type_req req;
+    struct ble_hs_conn *conn;
+    uint8_t uuid128[16];
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    rc = ble_hs_uuid_from_16bit(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
+    assert(rc == 0);
+
+    req.batq_start_handle = entry->disc_all_chars.prev_handle + 1;
+    req.batq_end_handle = entry->disc_all_chars.end_handle;
+
+    rc = ble_att_clt_tx_read_type(conn, &req, uuid128);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_disc_all_chars_cb(entry, rc, NULL);
+    return rc;
+}
+
+static void
+ble_gattc_err_disc_all_chars(struct ble_gattc_entry *entry, int status)
+{
+    if (status == BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)) {
+        /* Discovery is complete. */
+        status = 0;
+    }
+
+    ble_gattc_disc_all_chars_cb(entry, status, NULL);
+}
+
+void
+ble_gattc_rx_read_type_adata(struct ble_hs_conn *conn,
+                            struct ble_att_clt_adata *adata)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+    struct ble_gatt_chr chr;
+    uint16_t uuid16;
+    int cbrc;
+    int rc;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_CHARS, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    memset(&chr, 0, sizeof chr);
+    chr.decl_handle = adata->att_handle;
+
+    switch (adata->value_len) {
+    case BLE_GATT_CHR_DECL_SZ_16:
+        uuid16 = le16toh(adata->value + 3);
+        rc = ble_hs_uuid_from_16bit(uuid16, chr.uuid128);
+        if (rc != 0) {
+            rc = BLE_HS_EBADDATA;
+            goto done;
+        }
+        break;
+
+    case BLE_GATT_CHR_DECL_SZ_128:
+        memcpy(chr.uuid128, adata->value + 3, 16);
+        break;
+
+    default:
+        rc = BLE_HS_EBADDATA;
+        goto done;
+    }
+
+    entry->disc_all_chars.prev_handle = adata->att_handle;
+
+    chr.properties = adata->value[0];
+    chr.value_handle = le16toh(adata->value + 1);
+
+    rc = 0;
+
+done:
+    cbrc = ble_gattc_disc_all_chars_cb(entry, rc, &chr);
+    if (rc != 0 || cbrc != 0) {
+        ble_gattc_entry_remove_free(entry, prev);
+    }
+}
+
+void
+ble_gattc_rx_read_type_complete(struct ble_hs_conn *conn, int rc)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_DISC_ALL_CHARS, 1,
+                          &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    if (rc != 0 || entry->disc_all_chars.prev_handle ==
+                   entry->disc_all_chars.end_handle) {
+        /* Error or all services discovered. */
+        ble_gattc_disc_all_chars_cb(entry, rc, NULL);
+        ble_gattc_entry_remove_free(entry, prev);
+    } else {
+        /* Send follow-up request. */
+        ble_gattc_entry_set_pending(entry);
+    }
+}
+
+int
+ble_gattc_disc_all_chars(uint16_t conn_handle, uint16_t start_handle,
+                        uint16_t end_handle, ble_gatt_chr_fn *cb,
+                        void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_DISC_ALL_CHARS, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+    entry->disc_all_chars.prev_handle = start_handle - 1;
+    entry->disc_all_chars.end_handle = end_handle;
+    entry->disc_all_chars.cb = cb;
+    entry->disc_all_chars.cb_arg = cb_arg;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @read                                                                     *
+ *****************************************************************************/
+
+static int
+ble_gattc_read_cb(struct ble_gattc_entry *entry, int status,
+                 struct ble_gatt_attr *attr)
+{
+    int rc;
+
+    if (entry->read.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->read.cb(entry->conn_handle, status, attr,
+                            entry->read.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_read(struct ble_gattc_entry *entry)
+{
+    struct ble_att_read_req req;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    req.barq_handle = entry->read.handle;
+    rc = ble_att_clt_tx_read(conn, &req);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_read_cb(entry, rc, NULL);
+    return rc;
+}
+
+static void
+ble_gattc_err_read(struct ble_gattc_entry *entry, int status)
+{
+    ble_gattc_read_cb(entry, status, NULL);
+}
+
+void
+ble_gattc_rx_read_rsp(struct ble_hs_conn *conn, int status, void *value,
+                     int value_len)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+    struct ble_gatt_attr attr;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_READ, 1, &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    attr.handle = entry->read.handle;
+    attr.value_len = value_len;
+    attr.value = value;
+
+    ble_gattc_read_cb(entry, status, &attr);
+
+    /* The read operation only has a single request / response exchange. */
+    ble_gattc_entry_remove_free(entry, prev);
+}
+
+int
+ble_gattc_read(uint16_t conn_handle, uint16_t attr_handle,
+              ble_gatt_attr_fn *cb, void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_READ, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+    entry->read.handle = attr_handle;
+    entry->read.cb = cb;
+    entry->read.cb_arg = cb_arg;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @write no response                                                        *
+ *****************************************************************************/
+
+static int
+ble_gattc_write_cb(struct ble_gattc_entry *entry, int status)
+{
+    int rc;
+
+    if (entry->write.cb == NULL) {
+        rc = 0;
+    } else {
+        rc = entry->write.cb(entry->conn_handle, status, &entry->write.attr,
+                             entry->write.cb_arg);
+    }
+
+    return rc;
+}
+
+static int
+ble_gattc_kick_write_no_rsp(struct ble_gattc_entry *entry)
+{
+    struct ble_att_write_req req;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    req.bawq_handle = entry->write.attr.handle;
+    rc = ble_att_clt_tx_write_cmd(conn, &req, entry->write.attr.value,
+                                  entry->write.attr.value_len);
+    if (rc != 0) {
+        goto err;
+    }
+
+    /* No response expected; call callback immediately and return nonzero to
+     * indicate the entry should be freed.
+     */
+    ble_gattc_write_cb(entry, 0);
+
+    return 1;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_write_cb(entry, rc);
+    return rc;
+}
+
+int
+ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle, void *value,
+                      uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_WRITE_NO_RSP, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+
+    entry->write.attr.handle = attr_handle;
+    entry->write.attr.value = value;
+    entry->write.attr.value_len = value_len;
+    entry->write.cb = cb;
+    entry->write.cb_arg = cb_arg;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @write                                                                    *
+ *****************************************************************************/
+
+static int
+ble_gattc_kick_write(struct ble_gattc_entry *entry)
+{
+    struct ble_att_write_req req;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(entry->conn_handle);
+    if (conn == NULL) {
+        rc = BLE_HS_ENOTCONN;
+        goto err;
+    }
+
+    req.bawq_handle = entry->write.attr.handle;
+    rc = ble_att_clt_tx_write_req(conn, &req, entry->write.attr.value,
+                                  entry->write.attr.value_len);
+    if (rc != 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    if (ble_gattc_tx_postpone_chk(entry, rc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    ble_gattc_write_cb(entry, rc);
+    return rc;
+}
+
+static void
+ble_gattc_err_write(struct ble_gattc_entry *entry, int status)
+{
+    ble_gattc_write_cb(entry, status);
+}
+
+void
+ble_gattc_rx_write_rsp(struct ble_hs_conn *conn)
+{
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_find(conn->bhc_handle, BLE_GATT_OP_WRITE, 1, &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    ble_gattc_write_cb(entry, 0);
+
+    /* The write operation only has a single request / response exchange. */
+    ble_gattc_entry_remove_free(entry, prev);
+}
+
+int
+ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
+               uint16_t value_len, ble_gatt_attr_fn *cb, void *cb_arg)
+{
+    struct ble_gattc_entry *entry;
+    int rc;
+
+    rc = ble_gattc_new_entry(conn_handle, BLE_GATT_OP_WRITE, &entry);
+    if (rc != 0) {
+        return rc;
+    }
+
+    entry->write.attr.handle = attr_handle;
+    entry->write.attr.value = value;
+    entry->write.attr.value_len = value_len;
+    entry->write.cb = cb;
+    entry->write.cb_arg = cb_arg;
+
+    return 0;
+}
+
+/*****************************************************************************
+ * @misc                                                                     *
+ *****************************************************************************/
+
+void
+ble_gattc_wakeup(void)
+{
+    const struct ble_gattc_dispatch_entry *dispatch;
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+    struct ble_gattc_entry *next;
+    int rc;
+
+    prev = NULL;
+    entry = STAILQ_FIRST(&ble_gattc_list);
+    while (entry != NULL) {
+        next = STAILQ_NEXT(entry, next);
+
+        if (entry->flags & BLE_GATT_ENTRY_F_PENDING) {
+            dispatch = ble_gattc_dispatch_get(entry->op);
+            rc = dispatch->kick_cb(entry);
+            switch (rc) {
+            case 0:
+                /* Transmit succeeded.  Response expected. */
+                ble_gattc_entry_set_expecting(entry, prev);
+                /* Current entry got moved to back; old prev still valid. */
+                break;
+
+            case BLE_HS_EAGAIN:
+                /* Transmit failed due to resource shortage.  Reschedule. */
+                entry->flags &= ~BLE_GATT_ENTRY_F_PENDING;
+                /* Current entry remains; reseat prev. */
+                prev = entry;
+                break;
+
+            default:
+                /* Transmit failed.  Abort procedure. */
+                ble_gattc_entry_remove_free(entry, prev);
+                /* Current entry removed; old prev still valid. */
+                break;
+            }
+        } else {
+            prev = entry;
+        }
+
+        entry = next;
+    }
+}
+
+void
+ble_gattc_rx_err(uint16_t conn_handle, struct ble_att_error_rsp *rsp)
+{
+    const struct ble_gattc_dispatch_entry *dispatch;
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    entry = ble_gattc_find(conn_handle, BLE_GATT_OP_NONE, 1, &prev);
+    if (entry == NULL) {
+        /* Not expecting a response from this device. */
+        return;
+    }
+
+    dispatch = ble_gattc_dispatch_get(entry->op);
+    if (dispatch->err_cb != NULL) {
+        dispatch->err_cb(entry, BLE_HS_ERR_ATT_BASE + rsp->baep_error_code);
+    }
+
+    ble_gattc_entry_remove_free(entry, prev);
+}
+
+/**
+ * Called when a BLE connection ends.  Frees all GATT resources associated with
+ * the connection and cancels all relevant pending and in-progress GATT
+ * procedures.
+ */
+void
+ble_gattc_connection_broken(uint16_t conn_handle)
+{
+    const struct ble_gattc_dispatch_entry *dispatch;
+    struct ble_gattc_entry *entry;
+    struct ble_gattc_entry *prev;
+
+    while (1) {
+        entry = ble_gattc_find(conn_handle, BLE_GATT_OP_NONE, 0, &prev);
+        if (entry == NULL) {
+            break;
+        }
+
+        dispatch = ble_gattc_dispatch_get(entry->op);
+        dispatch->err_cb(entry, BLE_HS_ENOTCONN);
+
+        ble_gattc_entry_remove_free(entry, prev);
+    }
+}
+
+/**
+ * Called when a BLE connection transitions into a transmittable state.  Wakes
+ * up all congested GATT procedures associated with the connection.
+ */
+void
+ble_gattc_connection_txable(uint16_t conn_handle)
+{
+    struct ble_gattc_entry *entry;
+
+    STAILQ_FOREACH(entry, &ble_gattc_list, next) {
+        if (entry->conn_handle == conn_handle &&
+            entry->flags & BLE_GATT_ENTRY_F_CONGESTED) {
+
+            entry->flags &= ~BLE_GATT_ENTRY_F_CONGESTED;
+            if (ble_gattc_entry_can_pend(entry)) {
+                ble_gattc_entry_set_pending(entry);
+            }
+        }
+    }
+}
+
+int
+ble_gattc_init(void)
+{
+    int rc;
+
+    free(ble_gattc_entry_mem);
+
+    ble_gattc_entry_mem = malloc(
+        OS_MEMPOOL_BYTES(BLE_GATT_NUM_ENTRIES,
+                         sizeof (struct ble_gattc_entry)));
+    if (ble_gattc_entry_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&ble_gattc_entry_pool,
+                         BLE_GATT_NUM_ENTRIES,
+                         sizeof (struct ble_gattc_entry),
+                         ble_gattc_entry_mem,
+                         "ble_gattc_entry_pool");
+    if (rc != 0) {
+        goto err;
+    }
+
+    STAILQ_INIT(&ble_gattc_list);
+
+    os_callout_func_init(&ble_gattc_retx_timer, &ble_hs_evq,
+                         ble_gattc_retx_timer_exp, NULL);
+
+    return 0;
+
+err:
+    free(ble_gattc_entry_mem);
+    ble_gattc_entry_mem = NULL;
+
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 c7473d0..416e459 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -134,7 +134,7 @@ ble_hs_task_handler(void *arg)
             break;
 
         case BLE_HS_KICK_GATT_EVENT:
-            ble_gatt_wakeup();
+            ble_gattc_wakeup();
             break;
 
         default:
@@ -263,7 +263,7 @@ ble_hs_init(uint8_t prio)
         return rc;
     }
 
-    rc = ble_gatt_init();
+    rc = ble_gattc_init();
     if (rc != 0) {
         return rc;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/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 6c29ecf..4e0fc32 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -145,7 +145,7 @@ ble_hs_conn_chan_find(struct ble_hs_conn *conn, uint16_t cid)
 static void
 ble_hs_conn_txable_transition(struct ble_hs_conn *conn)
 {
-    ble_gatt_connection_txable(conn->bhc_handle);
+    ble_gattc_connection_txable(conn->bhc_handle);
 }
 
 void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/test/ble_gatt_conn_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_conn_test.c b/net/nimble/host/src/test/ble_gatt_conn_test.c
index c52b538..cefbc3a 100644
--- a/net/nimble/host/src/test/ble_gatt_conn_test.c
+++ b/net/nimble/host/src/test/ble_gatt_conn_test.c
@@ -146,7 +146,7 @@ TEST_CASE(ble_gatt_conn_test_disconnect)
     TEST_ASSERT_FATAL(rc == 0);
 
     /* Start the procedures. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
 
     /* Break the connections; verify callbacks got called. */
     ble_gatt_connection_broken(BLE_GATT_BREAK_TEST_DISC_SERVICE_HANDLE);
@@ -181,14 +181,14 @@ TEST_CASE(ble_gatt_conn_test_congestion)
     rc = ble_gatt_write(1, 0x1234, ble_gatt_conn_test_write_value,
                         sizeof ble_gatt_conn_test_write_value, NULL, NULL);
     TEST_ASSERT_FATAL(rc == 0);
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
 
     /* Ensure only one packet got sent. */
     TEST_ASSERT(conn->bhc_outstanding_pkts == 1);
 
     /* Additional wakeups should not trigger the second send. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
     TEST_ASSERT(conn->bhc_outstanding_pkts == 1);
 
@@ -202,7 +202,7 @@ TEST_CASE(ble_gatt_conn_test_congestion)
     TEST_ASSERT(conn->bhc_outstanding_pkts == 0);
 
     /* Now the second write should get sent. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
     TEST_ASSERT(conn->bhc_outstanding_pkts == 1);
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/2c857eba/net/nimble/host/src/test/ble_gatt_disc_c_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_disc_c_test.c b/net/nimble/host/src/test/ble_gatt_disc_c_test.c
index 19857c9..5a62704 100644
--- a/net/nimble/host/src/test/ble_gatt_disc_c_test.c
+++ b/net/nimble/host/src/test/ble_gatt_disc_c_test.c
@@ -62,7 +62,7 @@ ble_gatt_disc_c_test_misc_rx_all_rsp_once(
     int i;
 
     /* Send the pending ATT Read By Type Request. */
-    ble_gatt_wakeup();
+    ble_gattc_wakeup();
     ble_hs_process_tx_data_queue();
 
     if (chars[0].uuid16 != 0) {
@@ -134,7 +134,7 @@ ble_gatt_disc_c_test_misc_rx_all_rsp(struct ble_hs_conn *conn,
 
     if (chars[idx - 1].decl_handle != end_handle) {
         /* Send the pending ATT Request. */
-        ble_gatt_wakeup();
+        ble_gattc_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_TYPE_REQ,
                                         BLE_ATT_ERR_ATTR_NOT_FOUND,
                                         chars[idx - 1].decl_handle);
@@ -200,7 +200,7 @@ ble_gatt_disc_c_test_misc_all(uint16_t start_handle, uint16_t end_handle,
     conn = ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
 
     rc = ble_gatt_disc_all_chars(2, start_handle, end_handle,
-                                 ble_gatt_disc_c_test_misc_cb, NULL);
+                                  ble_gatt_disc_c_test_misc_cb, NULL);
     TEST_ASSERT(rc == 0);
 
     ble_gatt_disc_c_test_misc_rx_all_rsp(conn, end_handle, chars);



[3/6] incubator-mynewt-larva git commit: Initial implementation of GATT server.

Posted by cc...@apache.org.
Initial implementation of GATT server.


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

Branch: refs/heads/master
Commit: e949b8ab29bcb1f48f786c3c82e431f07cd49be3
Parents: 2c857eb
Author: Christopher Collins <cc...@gmail.com>
Authored: Wed Dec 23 13:45:27 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Wed Dec 23 15:33:32 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gatt.h |  27 +++++
 net/nimble/host/src/ble_gatt_priv.h     |   7 +-
 net/nimble/host/src/ble_gatts.c         | 147 +++++++++++++++++++++++++++
 3 files changed, 179 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/e949b8ab/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 7b59d26..cbb0338 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -18,10 +18,12 @@
 #define H_BLE_GATT_
 
 #include <inttypes.h>
+#include "host/ble_att.h"
 struct ble_hs_conn;
 struct ble_att_error_rsp;
 struct ble_att_clt_adata;
 
+/*** @client. */
 struct ble_gatt_service {
     uint16_t start_handle;
     uint16_t end_handle;
@@ -69,4 +71,29 @@ int ble_gatt_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
 int ble_gatt_exchange_mtu(uint16_t conn_handle);
 int ble_gatt_init(void);
 
+/*** @server. */
+struct ble_gatt_desc_def {
+    uint8_t *uuid128;
+    ble_att_svr_handle_func *access_cb;
+};
+
+struct ble_gatt_char_def {
+    uint8_t properties;
+    uint8_t *uuid128;
+    ble_att_svr_handle_func *access_cb;
+};
+
+#define BLE_GATT_SVC_TYPE_END       0
+#define BLE_GATT_SVC_TYPE_PRIMARY   1
+#define BLE_GATT_SVC_TYPE_SECONDAY  2
+
+struct ble_gatt_svc_def {
+    uint8_t type;
+    uint8_t *uuid128;
+    struct ble_gatt_svc_def **includes; /* Terminated with null. */
+    struct ble_gatt_char_def *characteristics;
+};
+
+int ble_gatt_register_services(const struct ble_gatt_svc_def *svcs);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/e949b8ab/net/nimble/host/src/ble_gatt_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatt_priv.h b/net/nimble/host/src/ble_gatt_priv.h
index 8a7e850..51dfc27 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -22,10 +22,10 @@
 #define BLE_GATT_CHR_DECL_SZ_16     5
 #define BLE_GATT_CHR_DECL_SZ_128    19
 
-/*** @gen */
+/*** @gen. */
 void ble_gatt_connection_broken(uint16_t conn_handle);
 
-/*** @client */
+/*** @client. */
 int ble_gattc_disc_all_services(uint16_t conn_handle,
                                ble_gatt_disc_service_fn *cb,
                                void *cb_arg);
@@ -64,4 +64,7 @@ void ble_gattc_connection_broken(uint16_t conn_handle);
 
 int ble_gattc_init(void);
 
+/*** @server. */
+int ble_gatts_register_services(const struct ble_gatt_svc_def *svcs);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/e949b8ab/net/nimble/host/src/ble_gatts.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatts.c b/net/nimble/host/src/ble_gatts.c
new file mode 100644
index 0000000..8dbc435
--- /dev/null
+++ b/net/nimble/host/src/ble_gatts.c
@@ -0,0 +1,147 @@
+/**
+ * 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 <stddef.h>
+#include "ble_hs_priv.h"
+#include "ble_gatt_priv.h"
+
+#define BLE_GATTS_MAX_SERVICES  32 /* XXX: Make this configurable. */
+
+struct ble_gatts_svc_entry {
+    const struct ble_gatt_svc_def *svc;
+    uint16_t handle;    /* 0 means unregistered. */
+};
+
+static struct ble_gatts_svc_entry
+    ble_gatts_svc_entries[BLE_GATTS_MAX_SERVICES];
+static int ble_gatts_num_svc_entries;
+
+static int
+ble_gatts_svc_access(struct ble_att_svr_entry *entry, uint8_t op,
+                     union ble_att_svr_handle_arg *arg)
+{
+    return 0;
+}
+
+static int
+ble_gatts_service_includes_satisfied(const struct ble_gatt_svc_def *svc)
+{
+    const struct ble_gatt_svc_def *incl;
+    int i;
+
+    if (svc->includes == NULL) {
+        /* No included services. */
+        return 1;
+    }
+
+    for (incl = *svc->includes; incl != NULL; incl++) {
+        for (i = 0; i < ble_gatts_num_svc_entries; i++) {
+            if (ble_gatts_svc_entries[i].handle == 0) {
+                return 0;
+            } else {
+                break;
+            }
+        }
+    }
+
+    return 1;
+}
+
+static int
+ble_gatts_register_service(const struct ble_gatt_svc_def *svc,
+                           uint16_t *out_handle)
+{
+    int rc;
+
+    if (!ble_gatts_service_includes_satisfied(svc)) {
+        return BLE_HS_EAGAIN;
+    }
+
+    rc = ble_att_svr_register(svc->uuid128, HA_FLAG_PERM_READ, out_handle,
+                              ble_gatts_svc_access);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+ble_gatts_register_round(int *out_num_registered)
+{
+    struct ble_gatts_svc_entry *entry;
+    uint16_t handle;
+    int rc;
+    int i;
+
+    *out_num_registered = 0;
+    for (i = 0; i < ble_gatts_num_svc_entries; i++) {
+        entry = ble_gatts_svc_entries + i;
+
+        if (entry->handle == 0) {
+            rc = ble_gatts_register_service(entry->svc, &handle);
+            switch (rc) {
+            case 0:
+                /* Service successfully registered. */
+                entry->handle = handle;
+                (*out_num_registered)++;
+                /* XXX: Call callback. */
+                break;
+
+            case BLE_HS_EAGAIN:
+                /* Service could not be registered due to unsatisfied includes.
+                 * Try again on the next itereation.
+                 */
+                break;
+
+            default:
+                return rc;
+            }
+        }
+    }
+
+    if (*out_num_registered == 0) {
+        return BLE_HS_EAPP; // XXX
+    }
+
+    return 0;
+}
+
+int
+ble_gatts_register_services(const struct ble_gatt_svc_def *svcs)
+{
+    int total_registered;
+    int cur_registered;
+    int rc;
+    int i;
+
+    for (i = 0; svcs[i].type != BLE_GATT_SVC_TYPE_END; i++) {
+        ble_gatts_svc_entries[i].svc = svcs + i;
+        ble_gatts_svc_entries[i].handle = 0;
+    }
+    ble_gatts_num_svc_entries = i;
+
+    total_registered = 0;
+    while (total_registered < ble_gatts_num_svc_entries) {
+        rc = ble_gatts_register_round(&cur_registered);
+        if (rc != 0) {
+            return rc;
+        }
+        total_registered += cur_registered;
+    }
+
+    return 0;
+}


[5/6] incubator-mynewt-larva git commit: Use a different set of op codes for ATT callbacks.

Posted by cc...@apache.org.
Use a different set of op codes for ATT callbacks.


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

Branch: refs/heads/master
Commit: 51bd2b46d2e44f76c42e932a7a66bf0d0ccc9461
Parents: 936b9e8
Author: Christopher Collins <cc...@gmail.com>
Authored: Wed Dec 23 14:31:12 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Wed Dec 23 15:33:49 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_att.h      |  3 +++
 net/nimble/host/src/ble_att_svr.c           | 12 ++++++------
 net/nimble/host/src/ble_gatts.c             |  2 +-
 net/nimble/host/src/test/ble_att_svr_test.c | 13 ++++++-------
 project/hostctlrtest/src/main.c             |  4 ++--
 5 files changed, 18 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/51bd2b46/net/nimble/host/include/host/ble_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_att.h b/net/nimble/host/include/host/ble_att.h
index 6522f0d..52046c5 100644
--- a/net/nimble/host/include/host/ble_att.h
+++ b/net/nimble/host/include/host/ble_att.h
@@ -77,6 +77,9 @@ union ble_att_svr_access_ctxt {
 #define HA_FLAG_AUTHENTICATION_REQ          (1 << 4)
 #define HA_FLAG_AUTHORIZATION_REQ           (1 << 5)
 
+#define BLE_ATT_ACCESS_OP_READ              1
+#define BLE_ATT_ACCESS_OP_WRITE             2
+
 /**
  * Handles a host attribute request.
  *

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/51bd2b46/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 17393f8..932e064 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -785,7 +785,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
             uuid16 = ble_hs_uuid_16bit(ha->ha_uuid);
             if (uuid16 == req->bavq_attr_type) {
                 rc = ha->ha_cb(ha->ha_handle_id, ha->ha_uuid,
-                               BLE_ATT_OP_READ_REQ, &arg, ha->ha_cb_arg);
+                               BLE_ATT_ACCESS_OP_READ, &arg, ha->ha_cb_arg);
                 if (rc != 0) {
                     rc = BLE_HS_EAPP;
                     goto done;
@@ -1010,7 +1010,7 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
             entry->ha_handle_id <= req->batq_end_handle) {
 
             rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
-                              BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
+                              BLE_ATT_ACCESS_OP_READ, &arg, entry->ha_cb_arg);
             if (rc != 0) {
                 *att_err = BLE_ATT_ERR_UNLIKELY;
                 *err_handle = entry->ha_handle_id;
@@ -1233,7 +1233,7 @@ ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     }
 
     rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
-                      BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
+                      BLE_ATT_ACCESS_OP_READ, &arg, entry->ha_cb_arg);
     if (rc != 0) {
         att_err = BLE_ATT_ERR_UNLIKELY;
         err_handle = req.barq_handle;
@@ -1275,7 +1275,7 @@ ble_att_svr_service_uuid(struct ble_att_svr_entry *entry, uint16_t *uuid16,
     int rc;
 
     rc = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
-                      BLE_ATT_OP_READ_REQ, &arg, entry->ha_cb_arg);
+                      BLE_ATT_ACCESS_OP_READ, &arg, entry->ha_cb_arg);
     if (rc != 0) {
         return rc;
     }
@@ -1651,7 +1651,7 @@ ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     os_mbuf_copydata(*rxom, 0, arg.ahc_write.attr_len,
                      arg.ahc_write.attr_data);
     att_err = entry->ha_cb(entry->ha_handle_id, entry->ha_uuid,
-                           BLE_ATT_OP_WRITE_REQ, &arg, entry->ha_cb_arg);
+                           BLE_ATT_ACCESS_OP_WRITE, &arg, entry->ha_cb_arg);
     if (att_err != 0) {
         err_handle = req.bawq_handle;
         rc = BLE_HS_EAPP;
@@ -1818,7 +1818,7 @@ ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
             arg.ahc_write.attr_data = ble_att_svr_flat_buf;
             arg.ahc_write.attr_len = buf_off;
             rc = attr->ha_cb(attr->ha_handle_id, attr->ha_uuid,
-                             BLE_ATT_OP_WRITE_REQ, &arg, attr->ha_cb_arg);
+                             BLE_ATT_ACCESS_OP_WRITE, &arg, attr->ha_cb_arg);
             if (rc != 0) {
                 *err_handle = entry->bape_handle;
                 return BLE_ATT_ERR_UNLIKELY;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/51bd2b46/net/nimble/host/src/ble_gatts.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gatts.c b/net/nimble/host/src/ble_gatts.c
index 7740610..3c71ba1 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -36,7 +36,7 @@ ble_gatts_svc_access(uint16_t handle_id, uint8_t *uuid128, uint8_t op,
 {
     const struct ble_gatt_svc_def *svc;
 
-    assert(op == BLE_ATT_OP_READ_REQ);
+    assert(op == BLE_ATT_ACCESS_OP_READ);
 
     svc = arg;
     ctxt->ahc_read.attr_data = svc->uuid128;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/51bd2b46/net/nimble/host/src/test/ble_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_svr_test.c b/net/nimble/host/src/test/ble_att_svr_test.c
index 45720fe..3562210 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -63,7 +63,7 @@ ble_att_svr_test_misc_attr_fn_r_1(uint16_t handle_id, uint8_t *uuid128,
                                   void *arg)
 {
     switch (op) {
-    case BLE_ATT_OP_READ_REQ:
+    case BLE_ATT_ACCESS_OP_READ:
         ctxt->ahc_read.attr_data = ble_att_svr_test_attr_r_1;
         ctxt->ahc_read.attr_len = ble_att_svr_test_attr_r_1_len;
         return 0;
@@ -80,7 +80,7 @@ ble_att_svr_test_misc_attr_fn_r_2(uint16_t handle_id, uint8_t *uuid128,
                                   void *arg)
 {
     switch (op) {
-    case BLE_ATT_OP_READ_REQ:
+    case BLE_ATT_ACCESS_OP_READ:
         ctxt->ahc_read.attr_data = ble_att_svr_test_attr_r_2;
         ctxt->ahc_read.attr_len = ble_att_svr_test_attr_r_2_len;
         return 0;
@@ -124,7 +124,7 @@ ble_att_svr_test_misc_attr_fn_r_group(uint16_t handle_id, uint8_t *uuid128,
 
     static uint8_t zeros[14];
 
-    if (op != BLE_ATT_OP_READ_REQ) {
+    if (op != BLE_ATT_ACCESS_OP_READ) {
         return -1;
     }
 
@@ -227,7 +227,7 @@ ble_att_svr_test_misc_attr_fn_w_1(uint16_t handle_id, uint8_t *uuid128,
                                   void *arg)
 {
     switch (op) {
-    case BLE_ATT_OP_WRITE_REQ:
+    case BLE_ATT_ACCESS_OP_WRITE:
         memcpy(ble_att_svr_test_attr_w_1, ctxt->ahc_write.attr_data,
                ctxt->ahc_write.attr_len);
         ble_att_svr_test_attr_w_1_len = ctxt->ahc_write.attr_len;
@@ -245,7 +245,7 @@ ble_att_svr_test_misc_attr_fn_w_2(uint16_t handle_id, uint8_t *uuid128,
                                   void *arg)
 {
     switch (op) {
-    case BLE_ATT_OP_WRITE_REQ:
+    case BLE_ATT_ACCESS_OP_WRITE:
         memcpy(ble_att_svr_test_attr_w_2, ctxt->ahc_write.attr_data,
                ctxt->ahc_write.attr_len);
         ble_att_svr_test_attr_w_2_len = ctxt->ahc_write.attr_len;
@@ -292,8 +292,7 @@ ble_att_svr_test_misc_verify_tx_err_rsp(struct ble_l2cap_chan *chan,
     TEST_ASSERT(rsp.baep_error_code == error_code);
 
     /* Remove the error response from the buffer. */
-    os_mbuf_adj(ble_hs_test_util_prev_tx,
-                BLE_ATT_ERROR_RSP_SZ);
+    os_mbuf_adj(ble_hs_test_util_prev_tx, BLE_ATT_ERROR_RSP_SZ);
 }
 
 static void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/51bd2b46/project/hostctlrtest/src/main.c
----------------------------------------------------------------------
diff --git a/project/hostctlrtest/src/main.c b/project/hostctlrtest/src/main.c
index be88129..87051a9 100755
--- a/project/hostctlrtest/src/main.c
+++ b/project/hostctlrtest/src/main.c
@@ -204,11 +204,11 @@ static uint16_t hostctlrtest_data2_handle;
 
 static int
 hostctlrtest_attr_cb(uint16_t handle_id, uint8_t *uuid128, uint8_t op,
-                     union ble_att_svr_handle_ctxt *ctxt, void *arg)
+                     union ble_att_svr_access_ctxt *ctxt, void *arg)
 {
     static uint8_t buf[128];
 
-    assert(op == BLE_ATT_OP_READ_REQ);
+    assert(op == BLE_ATT_ACCESS_OP_READ);
 
     if (handle_id == hostctlrtest_service_handle) {
         console_printf("reading service declaration");