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/01/06 02:30:43 UTC

incubator-mynewt-larva git commit: Server tx of notifications / indications.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 6545cc0bb -> c096844c8


Server tx of notifications / indications.


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

Branch: refs/heads/master
Commit: c096844c872eebae86b9025f1ff1b867a360c934
Parents: 6545cc0
Author: Christopher Collins <cc...@gmail.com>
Authored: Tue Jan 5 17:21:17 2016 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Tue Jan 5 17:30:21 2016 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_hs_test.h      |   1 +
 net/nimble/host/src/ble_gatt.c                  |  16 +
 net/nimble/host/src/ble_gatt_priv.h             |  15 +-
 net/nimble/host/src/ble_gattc.c                 | 102 ++----
 net/nimble/host/src/ble_gatts.c                 |  62 +++-
 net/nimble/host/src/test/ble_att_svr_test.c     |   4 +-
 .../host/src/test/ble_gatts_notify_test.c       | 332 +++++++++++++++++++
 net/nimble/host/src/test/ble_gatts_reg_test.c   |   4 +-
 net/nimble/host/src/test/ble_hs_test.c          |   1 +
 9 files changed, 440 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/net/nimble/host/include/host/ble_hs_test.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_hs_test.h b/net/nimble/host/include/host/ble_hs_test.h
index 7aebbbc..cf3d232 100644
--- a/net/nimble/host/include/host/ble_hs_test.h
+++ b/net/nimble/host/include/host/ble_hs_test.h
@@ -37,5 +37,6 @@ int ble_gatt_write_test_all(void);
 int ble_gatt_conn_test_all(void);
 int ble_hs_adv_test_all(void);
 int ble_gatts_reg_test_all(void);
+int ble_gatts_notify_test_all(void);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/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 dc99bb4..0214866 100644
--- a/net/nimble/host/src/ble_gatt.c
+++ b/net/nimble/host/src/ble_gatt.c
@@ -1,3 +1,19 @@
+/**
+ * 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 "ble_gatt_priv.h"
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/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 c222f79..f7f3748 100644
--- a/net/nimble/host/src/ble_gatt_priv.h
+++ b/net/nimble/host/src/ble_gatt_priv.h
@@ -24,17 +24,16 @@
 
 #define BLE_GATT_DSC_CLT_CFG_UUID16 0x2902
 
-struct ble_gatts_clt_cfg {
-    uint16_t chr_def_handle;
-    uint8_t flags;
-    uint8_t allowed;
-};
+typedef uint8_t ble_gatts_conn_flags;
 
 struct ble_gatts_conn {
     struct ble_gatts_clt_cfg *clt_cfgs;
     int num_clt_cfgs;
+    ble_gatts_conn_flags flags;
 };
 
+#define BLE_GATTS_CONN_F_INDICATION_TXED        0x01
+
 /*** @gen. */
 void ble_gatt_connection_broken(uint16_t conn_handle);
 
@@ -56,8 +55,7 @@ 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_indicate(uint16_t conn_handle, uint16_t chr_val_handle,
                        ble_gatt_attr_fn *cb, void *cb_arg);
-int ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_handle,
-                     ble_gatt_attr_fn *cb, void *cb_arg);
+int ble_gattc_notify(struct ble_hs_conn *conn, uint16_t chr_val_handle);
 
 int ble_gattc_exchange_mtu(uint16_t conn_handle);
 
@@ -80,14 +78,17 @@ void ble_gattc_rx_indicate_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_any_jobs(void);
 void ble_gattc_started(void);
 int ble_gattc_init(void);
 
 /*** @server. */
 #define BLE_GATTS_CLT_CFG_F_NOTIFY              0x0001
 #define BLE_GATTS_CLT_CFG_F_INDICATE            0x0002
+#define BLE_GATTS_CLT_CFG_F_UPDATED             0x0080 /* Internal only. */
 #define BLE_GATTS_CLT_CFG_F_RESERVED            0xfffc
 
+void ble_gatts_send_notifications(struct ble_hs_conn *conn);
 int ble_gatts_register_services(const struct ble_gatt_svc_def *svcs,
                                 ble_gatt_register_fn *register_cb,
                                 void *cb_arg);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/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
index b44113c..ffdf42b 100644
--- a/net/nimble/host/src/ble_gattc.c
+++ b/net/nimble/host/src/ble_gattc.c
@@ -79,12 +79,6 @@ struct ble_gattc_entry {
             struct ble_gatt_attr attr;
             ble_gatt_attr_fn *cb;
             void *cb_arg;
-        } notify;
-
-        struct {
-            struct ble_gatt_attr attr;
-            ble_gatt_attr_fn *cb;
-            void *cb_arg;
         } indicate;
     };
 };
@@ -100,9 +94,8 @@ struct ble_gattc_entry {
 #define BLE_GATT_OP_READ                        4
 #define BLE_GATT_OP_WRITE_NO_RSP                5
 #define BLE_GATT_OP_WRITE                       6
-#define BLE_GATT_OP_NOTIFY                      7
-#define BLE_GATT_OP_INDICATE                    8
-#define BLE_GATT_OP_MAX                         9
+#define BLE_GATT_OP_INDICATE                    7
+#define BLE_GATT_OP_MAX                         8
 
 static struct os_callout_func ble_gattc_heartbeat_timer;
 
@@ -116,7 +109,6 @@ 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 int ble_gattc_kick_notify(struct ble_gattc_entry *entry);
 static int ble_gattc_kick_indicate(struct ble_gattc_entry *entry);
 
 static void ble_gattc_err_mtu(struct ble_gattc_entry *entry, int status);
@@ -166,10 +158,6 @@ static const struct ble_gattc_dispatch_entry
         .kick_cb = ble_gattc_kick_write,
         .err_cb = ble_gattc_err_write,
     },
-    [BLE_GATT_OP_NOTIFY] = {
-        .kick_cb = ble_gattc_kick_notify,
-        .err_cb = NULL,
-    },
     [BLE_GATT_OP_INDICATE] = {
         .kick_cb = ble_gattc_kick_indicate,
         .err_cb = ble_gattc_err_indicate,
@@ -310,7 +298,7 @@ ble_gattc_entry_set_expecting(struct ble_gattc_entry *entry,
 
 static int
 ble_gattc_new_entry(uint16_t conn_handle, uint8_t op,
-                   struct ble_gattc_entry **entry)
+                    struct ble_gattc_entry **entry)
 {
     struct ble_hs_conn *conn;
 
@@ -1200,82 +1188,24 @@ ble_gattc_write(uint16_t conn_handle, uint16_t attr_handle, void *value,
  * $notify                                                                   *
  *****************************************************************************/
 
-static int
-ble_gattc_notify_cb(struct ble_gattc_entry *entry, int status)
-{
-    int rc;
-
-    if (entry->notify.cb == NULL) {
-        rc = 0;
-    } else {
-        rc = entry->notify.cb(entry->conn_handle, status, &entry->notify.attr,
-                              entry->notify.cb_arg);
-    }
-
-    return rc;
-}
-
-static int
-ble_gattc_kick_notify(struct ble_gattc_entry *entry)
+int
+ble_gattc_notify(struct ble_hs_conn *conn, uint16_t chr_val_handle)
 {
     struct ble_att_svr_access_ctxt ctxt;
     struct ble_att_notify_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;
-    }
-
-    rc = ble_att_svr_read_handle(NULL, entry->notify.attr.handle, &ctxt,
-                                 NULL);
+    rc = ble_att_svr_read_handle(NULL, chr_val_handle, &ctxt, NULL);
     if (rc != 0) {
-        goto err;
-    }
-    entry->notify.attr.value = ctxt.attr_data;
-    entry->notify.attr.value_len = ctxt.attr_len;
-
-    req.banq_handle = entry->notify.attr.handle;
-    rc = ble_att_clt_tx_notify(conn, &req, entry->notify.attr.value,
-                               entry->notify.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_notify_cb(entry, 0);
-
-    return 1;
-
-err:
-    if (ble_gattc_tx_postpone_chk(entry, rc)) {
-        return BLE_HS_EAGAIN;
+        return rc;
     }
 
-    ble_gattc_notify_cb(entry, rc);
-    return rc;
-}
-
-int
-ble_gattc_notify(uint16_t conn_handle, uint16_t chr_val_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_NOTIFY, &entry);
+    req.banq_handle = chr_val_handle;
+    rc = ble_att_clt_tx_notify(conn, &req, ctxt.attr_data, ctxt.attr_len);
     if (rc != 0) {
         return rc;
     }
 
-    entry->notify.attr.handle = chr_val_handle;
-    entry->notify.cb = cb;
-    entry->notify.cb_arg = cb_arg;
-
     return 0;
 }
 
@@ -1356,10 +1286,18 @@ ble_gattc_rx_indicate_rsp(struct ble_hs_conn *conn)
         return;
     }
 
+    /* Now that the confirmation has been received, we can send any subsequent
+     * indication.
+     */
+    conn->bhc_gatt_svr.flags &= ~BLE_GATTS_CONN_F_INDICATION_TXED;
+
     ble_gattc_indicate_cb(entry, 0);
 
     /* The indicate operation only has a single request / response exchange. */
     ble_gattc_entry_remove_free(entry, prev);
+
+    /* Send the next indication if one is pending. */
+    ble_gatts_send_notifications(conn);
 }
 
 int
@@ -1497,6 +1435,12 @@ ble_gattc_connection_txable(uint16_t conn_handle)
     }
 }
 
+int
+ble_gattc_any_jobs(void)
+{
+    return !STAILQ_EMPTY(&ble_gattc_list);
+}
+
 /**
  * XXX This function only exists because we can't set a timer before the OS
  * starts.  Maybe the OS issue can be fixed.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/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 1c06d4f..be9e11e 100644
--- a/net/nimble/host/src/ble_gatts.c
+++ b/net/nimble/host/src/ble_gatts.c
@@ -44,6 +44,12 @@ static os_membuf_t *ble_gatts_clt_cfg_mem;
 static struct os_mempool ble_gatts_clt_cfg_pool;
 static uint8_t ble_gatts_clt_cfg_inited;
 
+struct ble_gatts_clt_cfg {
+    uint16_t chr_def_handle;
+    uint8_t flags;
+    uint8_t allowed;
+};
+
 /** A cached array of handles for the configurable characteristics. */
 static struct ble_gatts_clt_cfg *ble_gatts_clt_cfgs;
 static int ble_gatts_num_cfgable_chrs;
@@ -218,6 +224,7 @@ ble_gatts_chr_val_access(uint16_t conn_handle, uint16_t attr_handle,
         return rc;
     }
 
+    att_ctxt->attr_data = gatt_ctxt.chr_access.data;
     att_ctxt->attr_len = gatt_ctxt.chr_access.len;
 
     return 0;
@@ -360,13 +367,13 @@ ble_gatts_clt_cfg_access(uint16_t conn_handle, uint16_t attr_handle,
     }
 
     switch (op) {
-    case BLE_GATT_ACCESS_OP_READ_DSC:
-        htole16(buf, clt_cfg->flags);
+    case BLE_ATT_ACCESS_OP_READ:
+        htole16(buf, clt_cfg->flags & ~BLE_GATTS_CLT_CFG_F_RESERVED);
         ctxt->attr_data = buf;
         ctxt->attr_len = sizeof buf;
         break;
 
-    case BLE_GATT_ACCESS_OP_WRITE_DSC:
+    case BLE_ATT_ACCESS_OP_WRITE:
         if (ctxt->attr_len != 2) {
             return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
         }
@@ -427,6 +434,7 @@ ble_gatts_dsc_access(uint16_t conn_handle, uint16_t attr_handle,
         return rc;
     }
 
+    att_ctxt->attr_data = gatt_ctxt.dsc_access.data;
     att_ctxt->attr_len = gatt_ctxt.dsc_access.len;
 
     return 0;
@@ -843,6 +851,45 @@ ble_gatts_conn_init(struct ble_gatts_conn *gatts_conn)
 }
 
 void
+ble_gatts_send_notifications(struct ble_hs_conn *conn)
+{
+    struct ble_gatts_clt_cfg *clt_cfg;
+    int rc;
+    int i;
+
+
+    /* Iterate through each configurable characteristic.  If a characteristic
+     * has been updated, try to send an indication or notification
+     * (never both).
+     */
+    for (i = 0; i < conn->bhc_gatt_svr.num_clt_cfgs; i++) {
+        clt_cfg = conn->bhc_gatt_svr.clt_cfgs + i;
+
+        if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_UPDATED) {
+            if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_INDICATE) {
+                if (!(conn->bhc_gatt_svr.flags &
+                      BLE_GATTS_CONN_F_INDICATION_TXED)) {
+
+                    rc = ble_gattc_indicate(conn->bhc_handle,
+                                            clt_cfg->chr_def_handle + 1,
+                                            NULL, NULL);
+                    if (rc == 0) {
+                        conn->bhc_gatt_svr.flags |=
+                            BLE_GATTS_CONN_F_INDICATION_TXED;
+                        clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_UPDATED;
+                    }
+                }
+            } else if (clt_cfg->flags & BLE_GATTS_CLT_CFG_F_NOTIFY) {
+                rc = ble_gattc_notify(conn, clt_cfg->chr_def_handle + 1);
+                if (rc == 0) {
+                    clt_cfg->flags &= ~BLE_GATTS_CLT_CFG_F_UPDATED;
+                }
+            }
+        }
+    }
+}
+
+void
 ble_gatts_chr_updated(uint16_t chr_def_handle)
 {
     struct ble_gatts_clt_cfg *clt_cfg;
@@ -866,10 +913,11 @@ ble_gatts_chr_updated(uint16_t chr_def_handle)
         clt_cfg = conn->bhc_gatt_svr.clt_cfgs + idx;
         assert(clt_cfg->chr_def_handle == chr_def_handle);
 
-        if (clt_cfg->flags & BLE_GATT_CHR_F_INDICATE) {
-            /* XXX: Schedule indication. */
-        } else if (clt_cfg->flags & BLE_GATT_CHR_F_NOTIFY) {
-            /* XXX: Schedule notification. */
+        if (clt_cfg->flags &
+            (BLE_GATTS_CLT_CFG_F_NOTIFY | BLE_GATTS_CLT_CFG_F_INDICATE)) {
+
+            clt_cfg->flags |= BLE_GATTS_CLT_CFG_F_UPDATED;
+            ble_gatts_send_notifications(conn);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/net/nimble/host/src/test/ble_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_svr_test.c b/net/nimble/host/src/test/ble_att_svr_test.c
index e1578d0..253622a 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -301,7 +301,7 @@ ble_att_svr_test_misc_verify_tx_err_rsp(struct ble_l2cap_chan *chan,
 
 static void
 ble_att_svr_test_misc_verify_tx_read_rsp(struct ble_l2cap_chan *chan,
-                                            uint8_t *attr_data, int attr_len)
+                                         uint8_t *attr_data, int attr_len)
 {
     uint8_t u8;
     int rc;
@@ -946,7 +946,7 @@ TEST_CASE(ble_att_svr_test_write)
     struct ble_att_write_req req;
     struct ble_l2cap_chan *chan;
     struct ble_hs_conn *conn;
-    uint8_t buf[BLE_ATT_READ_REQ_SZ + 8];
+    uint8_t buf[BLE_ATT_WRITE_REQ_BASE_SZ + 8];
     uint8_t uuid[16] = {0};
     int rc;
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/net/nimble/host/src/test/ble_gatts_notify_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatts_notify_test.c b/net/nimble/host/src/test/ble_gatts_notify_test.c
new file mode 100644
index 0000000..6e9ad53
--- /dev/null
+++ b/net/nimble/host/src/test/ble_gatts_notify_test.c
@@ -0,0 +1,332 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "testutil/testutil.h"
+#include "nimble/ble.h"
+#include "host/ble_uuid.h"
+#include "host/ble_hs_test.h"
+#include "ble_att_cmd.h"
+#include "ble_hs_priv.h"
+#include "ble_hs_conn.h"
+#include "ble_gatt_priv.h"
+#include "ble_hs_test_util.h"
+
+#define BLE_GATTS_NOTIFY_TEST_CHR_1_UUID    0x1111
+#define BLE_GATTS_NOTIFY_TEST_CHR_2_UUID    0x2222
+
+static int
+ble_gatts_notify_test_misc_access(uint16_t conn_handle,
+                                  uint16_t attr_handle, uint8_t op,
+                                  union ble_gatt_access_ctxt *ctxt,
+                                  void *arg);
+static void
+ble_gatts_notify_test_misc_reg_cb(uint8_t op,
+                                  union ble_gatt_register_ctxt *ctxt,
+                                  void *arg);
+
+static const struct ble_gatt_svc_def ble_gatts_notify_test_svcs[] = { {
+    .type = BLE_GATT_SVC_TYPE_PRIMARY,
+    .uuid128 = BLE_UUID16(0x1234),
+    .characteristics = (struct ble_gatt_chr_def[]) { {
+        .uuid128 = BLE_UUID16(BLE_GATTS_NOTIFY_TEST_CHR_1_UUID),
+        .access_cb = ble_gatts_notify_test_misc_access,
+        .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY |
+                 BLE_GATT_CHR_F_INDICATE,
+    }, {
+        .uuid128 = BLE_UUID16(BLE_GATTS_NOTIFY_TEST_CHR_2_UUID),
+        .access_cb = ble_gatts_notify_test_misc_access,
+        .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY |
+                 BLE_GATT_CHR_F_INDICATE,
+    }, {
+        .uuid128 = NULL,
+    } },
+}, {
+    .type = BLE_GATT_SVC_TYPE_END,
+} };
+
+
+static uint16_t ble_gatts_notify_test_chr_1_def_handle;
+static uint8_t ble_gatts_notify_test_chr_1_val[1024];
+static int ble_gatts_notify_test_chr_1_len;
+static uint16_t ble_gatts_notify_test_chr_2_def_handle;
+static uint8_t ble_gatts_notify_test_chr_2_val[1024];
+static int ble_gatts_notify_test_chr_2_len;
+
+static void
+ble_gatts_notify_test_misc_init(struct ble_hs_conn **conn,
+                                struct ble_l2cap_chan **att_chan)
+{
+    int rc;
+
+    ble_hs_test_util_init();
+
+    rc = ble_gatts_register_services(ble_gatts_notify_test_svcs,
+                                     ble_gatts_notify_test_misc_reg_cb, NULL);
+    TEST_ASSERT_FATAL(rc == 0);
+    TEST_ASSERT_FATAL(ble_gatts_notify_test_chr_1_def_handle != 0);
+    TEST_ASSERT_FATAL(ble_gatts_notify_test_chr_2_def_handle != 0);
+
+    ble_hs_test_util_create_conn(2, ((uint8_t[]){2,3,4,5,6,7,8,9}));
+    *conn = ble_hs_conn_find(2);
+    TEST_ASSERT_FATAL(*conn != NULL);
+
+    *att_chan = ble_hs_conn_chan_find(*conn, BLE_L2CAP_CID_ATT);
+    TEST_ASSERT_FATAL(*att_chan != NULL);
+}
+
+static void
+ble_gatts_notify_test_misc_enable_notify(struct ble_hs_conn *conn,
+                                         struct ble_l2cap_chan *chan,
+                                         uint16_t chr_def_handle,
+                                         uint16_t flags)
+{
+    struct ble_att_write_req req;
+    uint8_t buf[BLE_ATT_WRITE_REQ_BASE_SZ + 2];
+    int rc;
+
+    req.bawq_handle = chr_def_handle + 2;
+    rc = ble_att_write_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+
+    htole16(buf + BLE_ATT_WRITE_REQ_BASE_SZ, flags);
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+}
+
+static void
+ble_gatts_notify_test_misc_reg_cb(uint8_t op,
+                                  union ble_gatt_register_ctxt *ctxt,
+                                  void *arg)
+{
+    uint16_t uuid16;
+
+    if (op == BLE_GATT_REGISTER_OP_CHR) {
+        uuid16 = ble_uuid_128_to_16(ctxt->chr_reg.chr->uuid128);
+        switch (uuid16) {
+        case BLE_GATTS_NOTIFY_TEST_CHR_1_UUID:
+            ble_gatts_notify_test_chr_1_def_handle = ctxt->chr_reg.def_handle;
+            break;
+
+        case BLE_GATTS_NOTIFY_TEST_CHR_2_UUID:
+            ble_gatts_notify_test_chr_2_def_handle = ctxt->chr_reg.def_handle;
+            break;
+
+        default:
+            TEST_ASSERT_FATAL(0);
+            break;
+        }
+    }
+}
+
+static int
+ble_gatts_notify_test_misc_access(uint16_t conn_handle,
+                                  uint16_t attr_handle, uint8_t op,
+                                  union ble_gatt_access_ctxt *ctxt,
+                                  void *arg)
+{
+    TEST_ASSERT_FATAL(op == BLE_GATT_ACCESS_OP_READ_CHR);
+    TEST_ASSERT(conn_handle == 0xffff);
+
+    if (attr_handle == ble_gatts_notify_test_chr_1_def_handle + 1) {
+        TEST_ASSERT(ctxt->chr_access.chr ==
+                    &ble_gatts_notify_test_svcs[0].characteristics[0]);
+        ctxt->chr_access.data = ble_gatts_notify_test_chr_1_val;
+        ctxt->chr_access.len = ble_gatts_notify_test_chr_1_len;
+    } else if (attr_handle == ble_gatts_notify_test_chr_2_def_handle + 1) {
+        TEST_ASSERT(ctxt->chr_access.chr ==
+                    &ble_gatts_notify_test_svcs[0].characteristics[1]);
+        ctxt->chr_access.data = ble_gatts_notify_test_chr_2_val;
+        ctxt->chr_access.len = ble_gatts_notify_test_chr_2_len;
+    } else {
+        TEST_ASSERT(0);
+    }
+
+    return 0;
+}
+
+static void
+ble_gatts_notify_test_misc_rx_indicate_rsp(struct ble_hs_conn *conn,
+                                           struct ble_l2cap_chan *chan)
+{
+    uint8_t buf[BLE_ATT_INDICATE_RSP_SZ];
+    int rc;
+
+    rc = ble_att_indicate_rsp_write(buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_hs_test_util_l2cap_rx_payload_flat(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+}
+
+
+static void
+ble_gatts_notify_test_misc_verify_tx_n(struct ble_l2cap_chan *chan,
+                                       uint8_t *attr_data, int attr_len)
+{
+    uint8_t buf[1024];
+    struct ble_att_notify_req req;
+    int req_len;
+    int rc;
+    int i;
+
+    ble_gattc_wakeup();
+    ble_hs_process_tx_data_queue();
+
+    req_len = OS_MBUF_PKTLEN(ble_hs_test_util_prev_tx);
+    rc = os_mbuf_copydata(ble_hs_test_util_prev_tx, 0, req_len, buf);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    rc = ble_att_notify_req_parse(buf, req_len, &req);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; i < attr_len; i++) {
+        TEST_ASSERT(buf[BLE_ATT_NOTIFY_REQ_BASE_SZ + i] == attr_data[i]);
+    }
+}
+
+static void
+ble_gatts_notify_test_misc_verify_tx_i(struct ble_l2cap_chan *chan,
+                                       uint8_t *attr_data, int attr_len)
+{
+    uint8_t buf[1024];
+    struct ble_att_indicate_req req;
+    int req_len;
+    int rc;
+    int i;
+
+    ble_gattc_wakeup();
+    ble_hs_process_tx_data_queue();
+
+    req_len = OS_MBUF_PKTLEN(ble_hs_test_util_prev_tx);
+    rc = os_mbuf_copydata(ble_hs_test_util_prev_tx, 0, req_len, buf);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    rc = ble_att_indicate_req_parse(buf, req_len, &req);
+    TEST_ASSERT(rc == 0);
+
+    for (i = 0; i < attr_len; i++) {
+        TEST_ASSERT(buf[BLE_ATT_INDICATE_REQ_BASE_SZ + i] == attr_data[i]);
+    }
+}
+
+TEST_CASE(ble_gatts_notify_test_n)
+{
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+
+    ble_gatts_notify_test_misc_init(&conn, &chan);
+
+    /* Enable notifications on both characteristics. */
+    ble_gatts_notify_test_misc_enable_notify(
+        conn, chan, ble_gatts_notify_test_chr_1_def_handle,
+        BLE_GATTS_CLT_CFG_F_NOTIFY);
+    ble_gatts_notify_test_misc_enable_notify(
+        conn, chan, ble_gatts_notify_test_chr_2_def_handle,
+        BLE_GATTS_CLT_CFG_F_NOTIFY);
+
+    /* Update characteristic 1's value. */
+    ble_gatts_notify_test_chr_1_len = 1;
+    ble_gatts_notify_test_chr_1_val[0] = 0xab;
+    ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle);
+
+    /* Verify notification sent properly. */
+    ble_gatts_notify_test_misc_verify_tx_n(chan,
+                                           ble_gatts_notify_test_chr_1_val,
+                                           ble_gatts_notify_test_chr_1_len);
+
+    /* Update characteristic 2's value. */
+    ble_gatts_notify_test_chr_2_len = 16;
+    memcpy(ble_gatts_notify_test_chr_2_val,
+           ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}), 16);
+    ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle);
+
+    /* Verify notification sent properly. */
+    ble_gatts_notify_test_misc_verify_tx_n(chan,
+                                           ble_gatts_notify_test_chr_2_val,
+                                           ble_gatts_notify_test_chr_2_len);
+}
+
+TEST_CASE(ble_gatts_notify_test_i)
+{
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+
+    ble_gatts_notify_test_misc_init(&conn, &chan);
+
+    /* Enable indications on both characteristics. */
+    ble_gatts_notify_test_misc_enable_notify(
+        conn, chan, ble_gatts_notify_test_chr_1_def_handle,
+        BLE_GATTS_CLT_CFG_F_INDICATE);
+    ble_gatts_notify_test_misc_enable_notify(
+        conn, chan, ble_gatts_notify_test_chr_2_def_handle,
+        BLE_GATTS_CLT_CFG_F_INDICATE);
+
+    /* Update characteristic 1's value. */
+    ble_gatts_notify_test_chr_1_len = 1;
+    ble_gatts_notify_test_chr_1_val[0] = 0xab;
+    ble_gatts_chr_updated(ble_gatts_notify_test_chr_1_def_handle);
+
+    /* Verify indication sent properly. */
+    ble_gatts_notify_test_misc_verify_tx_i(
+        chan,
+        ble_gatts_notify_test_chr_1_val,
+        ble_gatts_notify_test_chr_1_len);
+
+    os_mbuf_free_chain(ble_hs_test_util_prev_tx);
+    ble_hs_test_util_prev_tx = NULL;
+
+    /* Update characteristic 2's value. */
+    ble_gatts_notify_test_chr_2_len = 16;
+    memcpy(ble_gatts_notify_test_chr_2_val,
+           ((uint8_t[]){0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}), 16);
+    ble_gatts_chr_updated(ble_gatts_notify_test_chr_2_def_handle);
+
+    /* Verify the second indication doesn't get sent until the first is
+     * confirmed.
+     */
+    TEST_ASSERT(ble_hs_test_util_prev_tx == NULL);
+
+    /* Receive the confirmation for the first indication. */
+    ble_gatts_notify_test_misc_rx_indicate_rsp(conn, chan);
+
+    /* Verify indication sent properly. */
+    ble_gatts_notify_test_misc_verify_tx_i(
+        chan,
+        ble_gatts_notify_test_chr_2_val,
+        ble_gatts_notify_test_chr_2_len);
+
+    /* Receive the confirmation for the second indication. */
+    ble_gatts_notify_test_misc_rx_indicate_rsp(conn, chan);
+
+    /* Verify no pending GATT jobs. */
+    TEST_ASSERT(!ble_gattc_any_jobs());
+}
+
+TEST_SUITE(ble_gatts_notify_suite)
+{
+    ble_gatts_notify_test_n();
+    ble_gatts_notify_test_i();
+}
+
+int
+ble_gatts_notify_test_all(void)
+{
+    ble_gatts_notify_suite();
+
+    return tu_any_failed;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/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 c5aa7c2..d37dd56 100644
--- a/net/nimble/host/src/test/ble_gatts_reg_test.c
+++ b/net/nimble/host/src/test/ble_gatts_reg_test.c
@@ -471,7 +471,7 @@ TEST_CASE(ble_gatts_reg_test_dsc_cb)
     } });
 }
 
-TEST_SUITE(ble_gatts_suite)
+TEST_SUITE(ble_gatts_reg_suite)
 {
     ble_gatts_reg_test_svc_return();
     ble_gatts_reg_test_chr_return();
@@ -485,7 +485,7 @@ TEST_SUITE(ble_gatts_suite)
 int
 ble_gatts_reg_test_all(void)
 {
-    ble_gatts_suite();
+    ble_gatts_reg_suite();
 
     return tu_any_failed;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/c096844c/net/nimble/host/src/test/ble_hs_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test.c b/net/nimble/host/src/test/ble_hs_test.c
index 01ed329..6878577 100644
--- a/net/nimble/host/src/test/ble_hs_test.c
+++ b/net/nimble/host/src/test/ble_hs_test.c
@@ -63,6 +63,7 @@ main(void)
     ble_gatt_conn_test_all();
     ble_hs_adv_test_all();
     ble_gatts_reg_test_all();
+    ble_gatts_notify_test_all();
 
     return tu_any_failed;
 }