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:31 UTC

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

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);