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 2016/07/13 20:55:20 UTC

[07/50] [abbrv] incubator-mynewt-core git commit: BLE Host - Lookup svc, chr, dsc handles by UUID.

BLE Host - Lookup svc,chr,dsc handles by UUID.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/787463b5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/787463b5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/787463b5

Branch: refs/heads/develop
Commit: 787463b5aac59a6d089621c00be14649cb3a97d5
Parents: 2997fff
Author: Christopher Collins <cc...@apache.org>
Authored: Wed Jun 29 10:10:57 2016 -0700
Committer: Christopher Collins <cc...@apache.org>
Committed: Mon Jul 11 16:43:32 2016 -0700

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gatt.h       |   7 +
 net/nimble/host/src/ble_att_priv.h            |   5 +-
 net/nimble/host/src/ble_att_svr.c             |  20 +-
 net/nimble/host/src/ble_gatts.c               | 253 ++++++++++++++++++---
 net/nimble/host/src/test/ble_gatts_reg_test.c | 252 +++++++++++++++++++-
 5 files changed, 483 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/787463b5/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 d805590..e69e59b 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -356,6 +356,13 @@ typedef void ble_gatt_register_fn(uint8_t op,
 int ble_gatts_register_svcs(const struct ble_gatt_svc_def *svcs,
                             ble_gatt_register_fn *register_cb,
                             void *cb_arg);
+
 void ble_gatts_chr_updated(uint16_t chr_def_handle);
 
+int ble_gatts_find_svc(const void *uuid128, uint16_t *out_handle);
+int ble_gatts_find_chr(const void *svc_uuid128, const void *chr_uuid128,
+                       uint16_t *out_def_handle, uint16_t *out_val_handle);
+int ble_gatts_find_dsc(const void *svc_uuid128, const void *chr_uuid128,
+                       const void *dsc_uuid128, uint16_t *out_dsc_handle);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/787463b5/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 5506c58..c1da676 100644
--- a/net/nimble/host/src/ble_att_priv.h
+++ b/net/nimble/host/src/ble_att_priv.h
@@ -156,9 +156,12 @@ int ble_att_init(void);
 /*** @svr */
 
 struct ble_att_svr_entry *
-ble_att_svr_find_by_uuid(struct ble_att_svr_entry *start_at, uint8_t *uuid);
+ble_att_svr_find_by_uuid(struct ble_att_svr_entry *start_at,
+                         const uint8_t *uuid,
+                         uint16_t end_handle);
 uint16_t ble_att_svr_prev_handle(void);
 int ble_att_svr_rx_mtu(uint16_t conn_handle, struct os_mbuf **om);
+struct ble_att_svr_entry *ble_att_svr_find_by_handle(uint16_t handle_id);
 int ble_att_svr_rx_find_info(uint16_t conn_handle, struct os_mbuf **rxom);
 int ble_att_svr_rx_find_type_value(uint16_t conn_handle,
                                    struct os_mbuf **rxom);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/787463b5/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 f7a5d36..8e57452 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -25,7 +25,9 @@
 #include "host/ble_uuid.h"
 #include "ble_hs_priv.h"
 
-static STAILQ_HEAD(, ble_att_svr_entry) ble_att_svr_list;
+STAILQ_HEAD(ble_att_svr_entry_list, ble_att_svr_entry);
+static struct ble_att_svr_entry_list ble_att_svr_list;
+
 static uint16_t ble_att_svr_id;
 
 static void *ble_att_svr_entry_mem;
@@ -159,7 +161,7 @@ ble_att_svr_find_by_handle(uint16_t handle_id)
  * Find a host attribute by UUID.
  *
  * @param uuid                  The ble_uuid_t to search for
- * @param ha_ptr                On input: Indicates the starting point of the
+ * @param prev                  On input: Indicates the starting point of the
  *                                  walk; null means start at the beginning of
  *                                  the list, non-null means start at the
  *                                  following entry.
@@ -170,7 +172,8 @@ ble_att_svr_find_by_handle(uint16_t handle_id)
  * @return                      0 on success; BLE_HS_ENOENT on not found.
  */
 struct ble_att_svr_entry *
-ble_att_svr_find_by_uuid(struct ble_att_svr_entry *prev, uint8_t *uuid)
+ble_att_svr_find_by_uuid(struct ble_att_svr_entry *prev, const uint8_t *uuid,
+                         uint16_t end_handle)
 {
     struct ble_att_svr_entry *entry;
 
@@ -180,7 +183,10 @@ ble_att_svr_find_by_uuid(struct ble_att_svr_entry *prev, uint8_t *uuid)
         entry = STAILQ_NEXT(prev, ha_next);
     }
 
-    for (; entry != NULL; entry = STAILQ_NEXT(entry, ha_next)) {
+    for (;
+         entry != NULL && entry->ha_handle_id <= end_handle;
+         entry = STAILQ_NEXT(entry, ha_next)) {
+
         if (memcmp(entry->ha_uuid, uuid, sizeof entry->ha_uuid) == 0) {
             return entry;
         }
@@ -1247,16 +1253,12 @@ ble_att_svr_build_read_type_rsp(uint16_t conn_handle,
     /* Find all matching attributes, writing a record for each. */
     entry = NULL;
     while (1) {
-        entry = ble_att_svr_find_by_uuid(entry, uuid128);
+        entry = ble_att_svr_find_by_uuid(entry, uuid128, req->batq_end_handle);
         if (entry == NULL) {
             rc = BLE_HS_ENOENT;
             break;
         }
 
-        if (entry->ha_handle_id > req->batq_end_handle) {
-            break;
-        }
-
         if (entry->ha_handle_id >= req->batq_start_handle) {
             ctxt.read.offset = 0;
             rc = ble_att_svr_read(conn_handle, entry, &ctxt, att_err);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/787463b5/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 0665758..82f21b2 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -35,7 +35,7 @@ struct ble_gatts_svc_entry {
 };
 
 static struct ble_gatts_svc_entry *ble_gatts_svc_entries;
-static int ble_gatts_num_svc_entries;
+static uint16_t ble_gatts_num_svc_entries;
 
 static os_membuf_t *ble_gatts_clt_cfg_mem;
 static struct os_mempool ble_gatts_clt_cfg_pool;
@@ -95,8 +95,6 @@ ble_gatts_inc_access(uint16_t conn_handle, uint16_t attr_handle,
                      uint8_t *uuid128, uint8_t op,
                      struct ble_att_svr_access_ctxt *ctxt, void *arg)
 {
-    static uint8_t buf[BLE_GATTS_INCLUDE_SZ];
-
     const struct ble_gatts_svc_entry *entry;
     uint16_t uuid16;
 
@@ -106,18 +104,17 @@ ble_gatts_inc_access(uint16_t conn_handle, uint16_t attr_handle,
 
     entry = arg;
 
-    htole16(buf + 0, entry->handle);
-    htole16(buf + 2, entry->end_group_handle);
+    htole16(ctxt->read.buf + 0, entry->handle);
+    htole16(ctxt->read.buf + 2, entry->end_group_handle);
 
     /* Only include the service UUID if it has a 16-bit representation. */
     uuid16 = ble_uuid_128_to_16(entry->svc->uuid128);
     if (uuid16 != 0) {
-        htole16(buf + 4, uuid16);
+        htole16(ctxt->read.buf + 4, uuid16);
         ctxt->read.len = 6;
     } else {
         ctxt->read.len = 4;
     }
-    ctxt->read.data = buf;
 
     return 0;
 }
@@ -323,7 +320,7 @@ ble_gatts_chr_val_access(uint16_t conn_handle, uint16_t attr_handle,
 }
 
 static int
-ble_gatts_find_svc(const struct ble_gatt_svc_def *svc)
+ble_gatts_find_svc_entry_idx(const struct ble_gatt_svc_def *svc)
 {
     int i;
 
@@ -348,7 +345,7 @@ ble_gatts_svc_incs_satisfied(const struct ble_gatt_svc_def *svc)
     }
 
     for (i = 0; svc->includes[i] != NULL; i++) {
-        idx = ble_gatts_find_svc(svc->includes[i]);
+        idx = ble_gatts_find_svc_entry_idx(svc->includes[i]);
         if (idx == -1 || ble_gatts_svc_entries[idx].handle == 0) {
             return 0;
         }
@@ -462,8 +459,9 @@ ble_gatts_dsc_is_sane(const struct ble_gatt_dsc_def *dsc)
 }
 
 static int
-ble_gatts_register_dsc(const struct ble_gatt_dsc_def *dsc,
+ble_gatts_register_dsc(const struct ble_gatt_svc_def *svc,
                        const struct ble_gatt_chr_def *chr,
+                       const struct ble_gatt_dsc_def *dsc,
                        uint16_t chr_def_handle,
                        ble_gatt_register_fn *register_cb, void *cb_arg)
 {
@@ -482,11 +480,10 @@ ble_gatts_register_dsc(const struct ble_gatt_dsc_def *dsc,
     }
 
     if (register_cb != NULL) {
-        register_ctxt.dsc.dsc_handle = dsc_handle;
-        register_ctxt.dsc.dsc_def = dsc;
-        register_ctxt.dsc.chr_def_handle = chr_def_handle;
-        register_ctxt.dsc.chr_val_handle = chr_def_handle + 1;
+        register_ctxt.dsc.handle = dsc_handle;
+        register_ctxt.dsc.svc_def = svc;
         register_ctxt.dsc.chr_def = chr;
+        register_ctxt.dsc.dsc_def = dsc;
         register_cb(BLE_GATT_REGISTER_OP_DSC, &register_ctxt, cb_arg);
     }
 
@@ -695,6 +692,13 @@ ble_gatts_register_chr(const struct ble_gatt_svc_def *svc,
         return BLE_HS_EINVAL;
     }
 
+    if (ble_gatts_chr_clt_cfg_allowed(chr) != 0) {
+        if (ble_gatts_num_cfgable_chrs > ble_hs_cfg.max_client_configs) {
+            return BLE_HS_ENOMEM;
+        }
+        ble_gatts_num_cfgable_chrs++;
+    }
+
     /* Register characteristic definition attribute (cast away const on
      * callback arg).
      */
@@ -723,7 +727,7 @@ ble_gatts_register_chr(const struct ble_gatt_svc_def *svc,
     if (register_cb != NULL) {
         register_ctxt.chr.def_handle = def_handle;
         register_ctxt.chr.val_handle = val_handle;
-        register_ctxt.svc.svc_def = svc;
+        register_ctxt.chr.svc_def = svc;
         register_ctxt.chr.chr_def = chr;
         register_cb(BLE_GATT_REGISTER_OP_CHR, &register_ctxt, cb_arg);
     }
@@ -739,7 +743,7 @@ ble_gatts_register_chr(const struct ble_gatt_svc_def *svc,
     /* Register each descriptor. */
     if (chr->descriptors != NULL) {
         for (dsc = chr->descriptors; dsc->uuid128 != NULL; dsc++) {
-            rc = ble_gatts_register_dsc(dsc, chr, def_handle, register_cb,
+            rc = ble_gatts_register_dsc(svc, chr, dsc, def_handle, register_cb,
                                         cb_arg);
             if (rc != 0) {
                 return rc;
@@ -829,7 +833,7 @@ ble_gatts_register_svc(const struct ble_gatt_svc_def *svc,
     /* Register each include. */
     if (svc->includes != NULL) {
         for (i = 0; svc->includes[i] != NULL; i++) {
-            idx = ble_gatts_find_svc(svc->includes[i]);
+            idx = ble_gatts_find_svc_entry_idx(svc->includes[i]);
             BLE_HS_DBG_ASSERT_EVAL(idx != -1);
 
             rc = ble_gatts_register_inc(ble_gatts_svc_entries + idx);
@@ -879,7 +883,7 @@ ble_gatts_register_round(int *out_num_registered, ble_gatt_register_fn *cb,
 
             case BLE_HS_EAGAIN:
                 /* Service could not be registered due to unsatisfied includes.
-                 * Try again on the next itereation.
+                 * Try again on the next iteration.
                  */
                 break;
 
@@ -959,26 +963,10 @@ ble_gatts_start(void)
     int idx;
     int rc;
 
-    rc = ble_uuid_16_to_128(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
-    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
-
-    /* Count the number of client-configurable characteristics. */
-    ble_gatts_num_cfgable_chrs = 0;
-    ha = NULL;
-    while ((ha = ble_att_svr_find_by_uuid(ha, uuid128)) != NULL) {
-        chr = ha->ha_cb_arg;
-        if (ble_gatts_chr_clt_cfg_allowed(chr) != 0) {
-            ble_gatts_num_cfgable_chrs++;
-        }
-    }
     if (ble_gatts_num_cfgable_chrs == 0) {
         return 0;
     }
 
-    if (ble_gatts_num_cfgable_chrs > ble_hs_cfg.max_client_configs) {
-        return BLE_HS_ENOMEM;
-    }
-
     /* Initialize client-configuration memory pool. */
     num_elems = ble_hs_cfg.max_client_configs / ble_gatts_num_cfgable_chrs;
     rc = os_mempool_init(&ble_gatts_clt_cfg_pool, num_elems,
@@ -997,9 +985,11 @@ ble_gatts_start(void)
     }
 
     /* Fill the cache. */
+    rc = ble_uuid_16_to_128(BLE_ATT_UUID_CHARACTERISTIC, uuid128);
+    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
     idx = 0;
     ha = NULL;
-    while ((ha = ble_att_svr_find_by_uuid(ha, uuid128)) != NULL) {
+    while ((ha = ble_att_svr_find_by_uuid(ha, uuid128, 0xffff)) != NULL) {
         chr = ha->ha_cb_arg;
         allowed_flags = ble_gatts_chr_clt_cfg_allowed(chr);
         if (allowed_flags != 0) {
@@ -1403,6 +1393,199 @@ ble_gatts_bonding_restored(uint16_t conn_handle)
     }
 }
 
+static struct ble_gatts_svc_entry *
+ble_gatts_find_svc_entry(const void *uuid128)
+{
+    struct ble_gatts_svc_entry *entry;
+    int i;
+
+    for (i = 0; i < ble_gatts_num_svc_entries; i++) {
+        entry = ble_gatts_svc_entries + i;
+        if (memcmp(uuid128, entry->svc->uuid128, 16) == 0) {
+            return entry;
+        }
+    }
+
+    return NULL;
+}
+
+static int
+ble_gatts_find_svc_chr_attr(const void *svc_uuid128, const void *chr_uuid128,
+                            struct ble_gatts_svc_entry **out_svc_entry,
+                            struct ble_att_svr_entry **out_att_chr)
+{
+    struct ble_gatts_svc_entry *svc_entry;
+    struct ble_att_svr_entry *att_svc;
+    struct ble_att_svr_entry *next;
+    struct ble_att_svr_entry *cur;
+    uint16_t uuid16;
+
+    svc_entry = ble_gatts_find_svc_entry(svc_uuid128);
+    if (svc_entry == NULL) {
+        return BLE_HS_ENOENT;
+    }
+
+    att_svc = ble_att_svr_find_by_handle(svc_entry->handle);
+    if (att_svc == NULL) {
+        return BLE_HS_EUNKNOWN;
+    }
+
+    cur = STAILQ_NEXT(att_svc, ha_next);
+    while (1) {
+        if (cur == NULL) {
+            /* Reached end of attribute list without a match. */
+            return BLE_HS_ENOENT;
+        }
+        next = STAILQ_NEXT(cur, ha_next);
+
+        if (cur->ha_handle_id == svc_entry->end_group_handle) {
+            /* Reached end of service without a match. */
+            return BLE_HS_ENOENT;
+        }
+
+        uuid16 = ble_uuid_128_to_16(cur->ha_uuid);
+        if (uuid16 == BLE_ATT_UUID_CHARACTERISTIC &&
+            next != NULL &&
+            memcmp(next->ha_uuid, chr_uuid128, 16) == 0) {
+
+            if (out_svc_entry != NULL) {
+                *out_svc_entry = svc_entry;
+            }
+            if (out_att_chr != NULL) {
+                *out_att_chr = next;
+            }
+            return 0;
+        }
+
+        cur = next;
+    }
+}
+
+/**
+ * Retrieves the attribute handle associated with a local GATT service.
+ *
+ * @param uuid128               The UUID of the service to look up.
+ * @param out_handle            On success, populated with the handle of the
+ *                                  service attribute.  Pass null if you don't
+ *                                  need this value.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_ENOENT if the specified service could
+ *                                  not be found.
+ */
+int
+ble_gatts_find_svc(const void *uuid128, uint16_t *out_handle)
+{
+    struct ble_gatts_svc_entry *entry;
+
+    entry = ble_gatts_find_svc_entry(uuid128);
+    if (entry == NULL) {
+        return BLE_HS_ENOENT;
+    }
+
+    if (out_handle != NULL) {
+        *out_handle = entry->handle;
+    }
+    return 0;
+}
+
+/**
+ * Retrieves the pair of attribute handles associated with a local GATT
+ * characteristic.
+ *
+ * @param svc_uuid128           The UUID of the parent service.
+ * @param chr_uuid128           The UUID of the characteristic to look up.
+ * @param out_def_handle        On success, populated with the handle
+ *                                  of the characteristic definition attribute.
+ *                                  Pass null if you don't need this value.
+ * @param out_val_handle        On success, populated with the handle
+ *                                  of the characteristic value attribute.
+ *                                  Pass null if you don't need this value.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_ENOENT if the specified service or
+ *                                  characteristic could not be found.
+ */
+int
+ble_gatts_find_chr(const void *svc_uuid128, const void *chr_uuid128,
+                   uint16_t *out_def_handle, uint16_t *out_val_handle)
+{
+    struct ble_att_svr_entry *att_chr;
+    int rc;
+
+    rc = ble_gatts_find_svc_chr_attr(svc_uuid128, chr_uuid128, NULL, &att_chr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (out_def_handle) {
+        *out_def_handle = att_chr->ha_handle_id - 1;
+    }
+    if (out_val_handle) {
+        *out_val_handle = att_chr->ha_handle_id;
+    }
+    return 0;
+}
+
+/**
+ * Retrieves the attribute handle associated with a local GATT descriptor.
+ *
+ * @param svc_uuid128           The UUID of the grandparent service.
+ * @param chr_uuid128           The UUID of the parent characteristic.
+ * @param dsc_uuid128           The UUID of the descriptor ro look up.
+ * @param out_handle            On success, populated with the handle
+ *                                  of the descripytor attribute.  Pass null if
+ *                                  you don't need this value.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_ENOENT if the specified service,
+ *                                  characteristic, or descriptor could not be
+ *                                  found.
+ */
+int
+ble_gatts_find_dsc(const void *svc_uuid128, const void *chr_uuid128,
+                   const void *dsc_uuid128, uint16_t *out_handle)
+{
+    struct ble_gatts_svc_entry *svc_entry;
+    struct ble_att_svr_entry *att_chr;
+    struct ble_att_svr_entry *cur;
+    uint16_t uuid16;
+    int rc;
+
+    rc = ble_gatts_find_svc_chr_attr(svc_uuid128, chr_uuid128, &svc_entry,
+                                     &att_chr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    cur = STAILQ_NEXT(att_chr, ha_next);
+    while (1) {
+        if (cur == NULL) {
+            /* Reached end of attribute list without a match. */
+            return BLE_HS_ENOENT;
+        }
+
+        if (cur->ha_handle_id == svc_entry->end_group_handle) {
+            /* Reached end of service without a match. */
+            return BLE_HS_ENOENT;
+        }
+
+        uuid16 = ble_uuid_128_to_16(cur->ha_uuid);
+        if (uuid16 == BLE_ATT_UUID_CHARACTERISTIC) {
+            /* Reached end of characteristic without a match. */
+            return BLE_HS_ENOENT;
+        }
+
+        if (memcmp(cur->ha_uuid, dsc_uuid128, 16) == 0) {
+            if (out_handle != NULL) {
+                *out_handle = cur->ha_handle_id;
+                return 0;
+            }
+        }
+        cur = STAILQ_NEXT(cur, ha_next);
+    }
+}
+
 static void
 ble_gatts_free_mem(void)
 {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/787463b5/net/nimble/host/src/test/ble_gatts_reg_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatts_reg_test.c b/net/nimble/host/src/test/ble_gatts_reg_test.c
index f6e5841..ff96f74 100644
--- a/net/nimble/host/src/test/ble_gatts_reg_test.c
+++ b/net/nimble/host/src/test/ble_gatts_reg_test.c
@@ -30,6 +30,12 @@
 struct ble_gatts_reg_test_entry {
     uint8_t op;
     uint8_t uuid128[16];
+    uint16_t handle;
+    uint16_t val_handle; /* If a characteristic. */
+
+    const struct ble_gatt_svc_def *svc;
+    const struct ble_gatt_chr_def *chr;
+    const struct ble_gatt_dsc_def *dsc;
 };
 
 static struct ble_gatts_reg_test_entry
@@ -54,19 +60,197 @@ ble_gatts_reg_test_misc_reg_cb(uint8_t op, union ble_gatt_register_ctxt *ctxt,
                       BLE_GATTS_REG_TEST_MAX_ENTRIES);
 
     entry = ble_gatts_reg_test_entries + ble_gatts_reg_test_num_entries++;
+    memset(entry, 0, sizeof *entry);
 
     entry->op = op;
     switch (op) {
     case BLE_GATT_REGISTER_OP_SVC:
         memcpy(entry->uuid128, ctxt->svc.svc_def->uuid128, 16);
+        entry->handle = ctxt->svc.handle;
+        entry->svc = ctxt->svc.svc_def;
         break;
 
     case BLE_GATT_REGISTER_OP_CHR:
         memcpy(entry->uuid128, ctxt->chr.chr_def->uuid128, 16);
+        entry->handle = ctxt->chr.def_handle;
+        entry->val_handle = ctxt->chr.val_handle;
+        entry->svc = ctxt->chr.svc_def;
+        entry->chr = ctxt->chr.chr_def;
         break;
 
     case BLE_GATT_REGISTER_OP_DSC:
         memcpy(entry->uuid128, ctxt->dsc.dsc_def->uuid128, 16);
+        entry->handle = ctxt->dsc.handle;
+        entry->svc = ctxt->dsc.svc_def;
+        entry->chr = ctxt->dsc.chr_def;
+        entry->dsc = ctxt->dsc.dsc_def;
+        break;
+
+    default:
+        TEST_ASSERT(0);
+        break;
+    }
+}
+
+static void
+ble_gatts_reg_test_misc_lookup_good(struct ble_gatts_reg_test_entry *entry)
+{
+    uint16_t chr_def_handle;
+    uint16_t chr_val_handle;
+    uint16_t svc_handle;
+    uint16_t dsc_handle;
+    int rc;
+
+    switch (entry->op) {
+    case BLE_GATT_REGISTER_OP_SVC:
+        rc = ble_gatts_find_svc(entry->uuid128, &svc_handle);
+        TEST_ASSERT_FATAL(rc == 0);
+        TEST_ASSERT(svc_handle == entry->handle);
+        break;
+
+    case BLE_GATT_REGISTER_OP_CHR:
+        rc = ble_gatts_find_chr(entry->svc->uuid128, entry->chr->uuid128,
+                                &chr_def_handle, &chr_val_handle);
+        TEST_ASSERT_FATAL(rc == 0);
+        TEST_ASSERT(chr_def_handle == entry->handle);
+        TEST_ASSERT(chr_val_handle == entry->val_handle);
+        break;
+
+    case BLE_GATT_REGISTER_OP_DSC:
+        rc = ble_gatts_find_dsc(entry->svc->uuid128, entry->chr->uuid128,
+                                entry->dsc->uuid128, &dsc_handle);
+        break;
+
+    default:
+        TEST_ASSERT(0);
+        break;
+    }
+}
+
+static void
+ble_gatts_reg_test_misc_lookup_bad(struct ble_gatts_reg_test_entry *entry)
+{
+    struct ble_gatts_reg_test_entry *cur;
+    uint8_t wrong_uuid[16];
+    int rc;
+    int i;
+
+    switch (entry->op) {
+    case BLE_GATT_REGISTER_OP_SVC:
+        /* Wrong service UUID. */
+        memcpy(wrong_uuid, entry->svc->uuid128, 16);
+        wrong_uuid[15]++;
+        rc = ble_gatts_find_svc(wrong_uuid, NULL);
+        TEST_ASSERT(rc == BLE_HS_ENOENT);
+        break;
+
+    case BLE_GATT_REGISTER_OP_CHR:
+        /* Correct service UUID, wrong characteristic UUID. */
+        memcpy(wrong_uuid, entry->chr->uuid128, 16);
+        wrong_uuid[15]++;
+        rc = ble_gatts_find_chr(entry->svc->uuid128, wrong_uuid, NULL, NULL);
+        TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+        /* Incorrect service UUID, correct characteristic UUID. */
+        memcpy(wrong_uuid, entry->svc->uuid128, 16);
+        wrong_uuid[15]++;
+        rc = ble_gatts_find_chr(wrong_uuid, entry->chr->uuid128, NULL, NULL);
+        TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+        /* Existing (but wrong) service, correct characteristic UUID. */
+        for (i = 0; i < ble_gatts_reg_test_num_entries; i++) {
+            cur = ble_gatts_reg_test_entries + i;
+            switch (cur->op) {
+            case BLE_GATT_REGISTER_OP_SVC:
+                if (cur->svc != entry->svc) {
+                    rc = ble_gatts_find_chr(cur->svc->uuid128,
+                                            entry->chr->uuid128,
+                                            NULL, NULL);
+                    TEST_ASSERT(rc == BLE_HS_ENOENT);
+                }
+                break;
+
+            case BLE_GATT_REGISTER_OP_CHR:
+                /* Characteristic that isn't in this service. */
+                if (cur->svc != entry->svc) {
+                    rc = ble_gatts_find_chr(entry->svc->uuid128,
+                                            cur->chr->uuid128,
+                                            NULL, NULL);
+                    TEST_ASSERT(rc == BLE_HS_ENOENT);
+                }
+                break;
+
+            case BLE_GATT_REGISTER_OP_DSC:
+                /* Use descriptor UUID instead of characteristic UUID. */
+                rc = ble_gatts_find_chr(entry->svc->uuid128,
+                                        cur->dsc->uuid128,
+                                        NULL, NULL);
+                TEST_ASSERT(rc == BLE_HS_ENOENT);
+                break;
+
+            default:
+                TEST_ASSERT(0);
+                break;
+            }
+        }
+        break;
+
+    case BLE_GATT_REGISTER_OP_DSC:
+        /* Correct svc/chr UUID, wrong dsc UUID. */
+        memcpy(wrong_uuid, entry->dsc->uuid128, 16);
+        wrong_uuid[15]++;
+        rc = ble_gatts_find_dsc(entry->svc->uuid128, entry->chr->uuid128,
+                                wrong_uuid, NULL);
+        TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+        /* Incorrect svc UUID, correct chr/dsc UUID. */
+        memcpy(wrong_uuid, entry->svc->uuid128, 16);
+        wrong_uuid[15]++;
+        rc = ble_gatts_find_dsc(wrong_uuid, entry->chr->uuid128,
+                                entry->dsc->uuid128, NULL);
+        TEST_ASSERT(rc == BLE_HS_ENOENT);
+
+        for (i = 0; i < ble_gatts_reg_test_num_entries; i++) {
+            cur = ble_gatts_reg_test_entries + i;
+            switch (cur->op) {
+            case BLE_GATT_REGISTER_OP_SVC:
+                /* Existing (but wrong) svc, correct chr/dsc UUID. */
+                if (cur->svc != entry->svc) {
+                    rc = ble_gatts_find_dsc(cur->svc->uuid128,
+                                            entry->chr->uuid128,
+                                            entry->dsc->uuid128,
+                                            NULL);
+                    TEST_ASSERT(rc == BLE_HS_ENOENT);
+                }
+                break;
+
+            case BLE_GATT_REGISTER_OP_CHR:
+                /* Existing (but wrong) svc/chr, correct dsc UUID. */
+                if (cur->chr != entry->chr) {
+                    rc = ble_gatts_find_dsc(cur->svc->uuid128,
+                                            cur->chr->uuid128,
+                                            entry->dsc->uuid128,
+                                            NULL);
+                    TEST_ASSERT(rc == BLE_HS_ENOENT);
+                }
+                break;
+
+            case BLE_GATT_REGISTER_OP_DSC:
+                /* Descriptor that isn't in this characteristic. */
+                if (cur->chr != entry->chr) {
+                    rc = ble_gatts_find_dsc(cur->svc->uuid128,
+                                            cur->chr->uuid128,
+                                            entry->dsc->uuid128,
+                                            NULL);
+                    TEST_ASSERT(rc == BLE_HS_ENOENT);
+                }
+                break;
+
+            default:
+                TEST_ASSERT(0);
+                break;
+            }
+        }
         break;
 
     default:
@@ -84,11 +268,25 @@ ble_gatts_reg_test_misc_verify_entry(uint8_t op, const uint8_t *uuid128)
     for (i = 0; i < ble_gatts_reg_test_num_entries; i++) {
         entry = ble_gatts_reg_test_entries + i;
         if (entry->op == op && memcmp(entry->uuid128, uuid128, 16) == 0) {
-            return;
+            break;
         }
     }
+    TEST_ASSERT_FATAL(entry != NULL);
 
-    TEST_ASSERT(0);
+    /* Verify that characteristic value handle was properly assigned at
+     * registration.
+     */
+    if (op == BLE_GATT_REGISTER_OP_CHR) {
+        TEST_ASSERT(*entry->chr->val_handle == entry->val_handle);
+    }
+
+    /* Verify that the entry can be looked up. */
+    ble_gatts_reg_test_misc_lookup_good(entry);
+
+    /* Verify that "barely incorrect" UUID information doesn't retrieve any
+     * handles.
+     */
+    ble_gatts_reg_test_misc_lookup_bad(entry);
 }
 
 static int
@@ -337,6 +535,8 @@ TEST_CASE(ble_gatts_reg_test_svc_cb)
 
 TEST_CASE(ble_gatts_reg_test_chr_cb)
 {
+    uint16_t val_handles[16];
+
     /*** 1 characteristic. */
     ble_gatts_reg_test_misc_svcs((struct ble_gatt_svc_def[]) { {
         .type = BLE_GATT_SVC_TYPE_PRIMARY,
@@ -345,6 +545,7 @@ TEST_CASE(ble_gatts_reg_test_chr_cb)
             .uuid128 = BLE_UUID16(0x1111),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 0,
         }, {
             0
         } },
@@ -360,10 +561,12 @@ TEST_CASE(ble_gatts_reg_test_chr_cb)
             .uuid128 = BLE_UUID16(0x1111),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 0,
         }, {
             .uuid128 = BLE_UUID16(0x2222),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_WRITE,
+            .val_handle = val_handles + 1,
         }, {
             0
         } },
@@ -374,6 +577,7 @@ TEST_CASE(ble_gatts_reg_test_chr_cb)
             .uuid128 = BLE_UUID16(0x3333),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 2,
         }, {
             0
         } },
@@ -384,6 +588,8 @@ TEST_CASE(ble_gatts_reg_test_chr_cb)
 
 TEST_CASE(ble_gatts_reg_test_dsc_cb)
 {
+    uint16_t val_handles[16];
+
     /*** 1 descriptor. */
     ble_gatts_reg_test_misc_svcs((struct ble_gatt_svc_def[]) { {
         .type = BLE_GATT_SVC_TYPE_PRIMARY,
@@ -392,8 +598,9 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
             .uuid128 = BLE_UUID16(0x1111),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 0,
             .descriptors = (struct ble_gatt_dsc_def[]) { {
-                .uuid128 = BLE_UUID16(0xaaaa),
+                .uuid128 = BLE_UUID16(0x111a),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {
@@ -406,7 +613,7 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
         0
     } });
 
-    /*** 5 descriptors. */
+    /*** 5+ descriptors. */
     ble_gatts_reg_test_misc_svcs((struct ble_gatt_svc_def[]) { {
         .type = BLE_GATT_SVC_TYPE_PRIMARY,
         .uuid128 = BLE_UUID16(0x1234),
@@ -414,8 +621,9 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
             .uuid128 = BLE_UUID16(0x1111),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 0,
             .descriptors = (struct ble_gatt_dsc_def[]) { {
-                .uuid128 = BLE_UUID16(0xaaaa),
+                .uuid128 = BLE_UUID16(0x111a),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {
@@ -425,6 +633,7 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
             .uuid128 = BLE_UUID16(0x2222),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_WRITE,
+            .val_handle = val_handles + 1,
         }, {
             0
         } },
@@ -435,20 +644,45 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
             .uuid128 = BLE_UUID16(0x3333),
             .access_cb = ble_gatts_reg_test_misc_dummy_access,
             .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 2,
+            .descriptors = (struct ble_gatt_dsc_def[]) { {
+                .uuid128 = BLE_UUID16(0x333a),
+                .att_flags = 5,
+                .access_cb = ble_gatts_reg_test_misc_dummy_access,
+            }, {
+                .uuid128 = BLE_UUID16(0x333b),
+                .att_flags = 5,
+                .access_cb = ble_gatts_reg_test_misc_dummy_access,
+            }, {
+                .uuid128 = BLE_UUID16(0x333c),
+                .att_flags = 5,
+                .access_cb = ble_gatts_reg_test_misc_dummy_access,
+            }, {
+                .uuid128 = BLE_UUID16(0x333e),
+                .att_flags = 5,
+                .access_cb = ble_gatts_reg_test_misc_dummy_access,
+            }, {
+                0
+            } },
+        }, {
+            .uuid128 = BLE_UUID16(0x4444),
+            .access_cb = ble_gatts_reg_test_misc_dummy_access,
+            .flags = BLE_GATT_CHR_F_READ,
+            .val_handle = val_handles + 3,
             .descriptors = (struct ble_gatt_dsc_def[]) { {
-                .uuid128 = BLE_UUID16(0xaaab),
+                .uuid128 = BLE_UUID16(0x444a),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {
-                .uuid128 = BLE_UUID16(0xaaac),
+                .uuid128 = BLE_UUID16(0x444b),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {
-                .uuid128 = BLE_UUID16(0xaaad),
+                .uuid128 = BLE_UUID16(0x444c),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {
-                .uuid128 = BLE_UUID16(0xaaae),
+                .uuid128 = BLE_UUID16(0x444e),
                 .att_flags = 5,
                 .access_cb = ble_gatts_reg_test_misc_dummy_access,
             }, {