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/23 03:05:35 UTC

[1/2] incubator-mynewt-larva git commit: Add some more advertisement data fields.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 961bcd69b -> 14f9937e2


Add some more advertisement data fields.


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

Branch: refs/heads/master
Commit: 57949307617e9a53ea9255f7f5ec5fc8c5aec443
Parents: 961bcd6
Author: Christopher Collins <cc...@gmail.com>
Authored: Tue Dec 22 14:02:36 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Tue Dec 22 18:04:04 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_hs.h      |  32 ++++-
 net/nimble/host/src/ble_gap_conn.c         |  13 +-
 net/nimble/host/src/ble_hs_adv.c           | 139 ++++++++++++++++--
 net/nimble/host/src/ble_hs_adv.h           |  17 +--
 net/nimble/host/src/test/ble_hs_adv_test.c | 183 +++++++++++++++++++++++-
 5 files changed, 354 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/57949307/net/nimble/host/include/host/ble_hs.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_hs.h b/net/nimble/host/include/host/ble_hs.h
index 5ffc01b..c7cb09c 100644
--- a/net/nimble/host/include/host/ble_hs.h
+++ b/net/nimble/host/include/host/ble_hs.h
@@ -39,15 +39,43 @@ struct ble_hs_cfg {
 };
 extern struct ble_hs_cfg ble_hs_cfg;
 
+
+#define BLE_HS_ADV_LE_ROLE_PERIPH               0x00
+#define BLE_HS_ADV_LE_ROLE_CENTRAL              0x01
+#define BLE_HS_ADV_LE_ROLE_BOTH_PERIPH_PREF     0x02
+#define BLE_HS_ADV_LE_ROLE_BOTH_CENTRAL_PREF    0x03
+
 struct ble_hs_adv_fields {
+    /*** 0x01 - Flags. */
     uint8_t flags;
 
-    uint8_t tx_pwr_lvl;
-    unsigned tx_pwr_lvl_is_present:1;
+    /*** 0x02,0x03 - 16-bit service class UUIDs. */
+    uint16_t *uuids16;
+    uint8_t num_uuids16;
+    unsigned uuids16_is_complete:1;
+
+    /*** 0x04,0x05 - 32-bit service class UUIDs. */
+    uint32_t *uuids32;
+    uint8_t num_uuids32;
+    unsigned uuids32_is_complete:1;
 
+    /*** 0x06,0x07 - 128-bit service class UUIDs. */
+    void *uuids128;
+    uint8_t num_uuids128;
+    unsigned uuids128_is_complete:1;
+
+    /*** 0x08,0x09 - Local name. */
     uint8_t *name;
     uint8_t name_len;
     unsigned name_is_complete:1;
+
+    /*** 0x0a - Tx power level. */
+    uint8_t tx_pwr_lvl;
+    unsigned tx_pwr_lvl_is_present:1;
+
+    /*** 0x1c - LE role. */
+    uint8_t le_role;
+    unsigned le_role_is_present:1;
 };
 
 int ble_hs_init(uint8_t prio);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/57949307/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 4eba4b2..95ee413 100644
--- a/net/nimble/host/src/ble_gap_conn.c
+++ b/net/nimble/host/src/ble_gap_conn.c
@@ -680,17 +680,16 @@ ble_gap_conn_adv_data_tx(void *arg)
     /* Encode the flags AD field if it is nonzero. */
     adv_data_len = ble_gap_conn_adv_data_len;
     if (flags != 0) {
-        rc = ble_hs_adv_set_one_field(BLE_HS_ADV_TYPE_FLAGS, 1,
-                                      &flags, ble_gap_conn_adv_data,
-                                      &adv_data_len, BLE_HCI_MAX_ADV_DATA_LEN);
+        rc = ble_hs_adv_set_flat(BLE_HS_ADV_TYPE_FLAGS, 1, &flags,
+                                 ble_gap_conn_adv_data, &adv_data_len,
+                                 BLE_HCI_MAX_ADV_DATA_LEN);
         assert(rc == 0);
     }
 
     /* Encode the transmit power AD field. */
-    rc = ble_hs_adv_set_one_field(BLE_HS_ADV_TYPE_TX_PWR_LEVEL, 1,
-                                  &ble_gap_conn_tx_pwr_lvl,
-                                  ble_gap_conn_adv_data,
-                                  &adv_data_len, BLE_HCI_MAX_ADV_DATA_LEN);
+    rc = ble_hs_adv_set_flat(BLE_HS_ADV_TYPE_TX_PWR_LEVEL, 1,
+                             &ble_gap_conn_tx_pwr_lvl, ble_gap_conn_adv_data,
+                             &adv_data_len, BLE_HCI_MAX_ADV_DATA_LEN);
     assert(rc == 0);
 
     ble_hci_ack_set_callback(ble_gap_conn_adv_ack, NULL);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/57949307/net/nimble/host/src/ble_hs_adv.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_adv.c b/net/nimble/host/src/ble_hs_adv.c
index 0be75ea..c33c688 100644
--- a/net/nimble/host/src/ble_hs_adv.c
+++ b/net/nimble/host/src/ble_hs_adv.c
@@ -17,25 +17,81 @@
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
+#include "nimble/ble.h"
 #include "ble_hs_adv.h"
 #include "ble_hs_priv.h"
 
-int
-ble_hs_adv_set_one_field(uint8_t type, uint8_t data_len, void *data,
-                         uint8_t *dst, uint8_t *dst_len, uint8_t max_len)
+static int
+ble_hs_adv_set_hdr(uint8_t type, uint8_t data_len, uint8_t max_len,
+                   uint8_t *dst, uint8_t *dst_len)
 {
-    int new_len;
-
-    new_len = *dst_len + 2 + data_len;
-    if (new_len > max_len) {
+    if (*dst_len + 2 + data_len > max_len) {
         return BLE_HS_EMSGSIZE;
     }
 
     dst[*dst_len] = data_len + 1;
     dst[*dst_len + 1] = type;
-    memcpy(dst + *dst_len + 2, data, data_len);
 
-    *dst_len = new_len;
+    *dst_len += 2;
+
+    return 0;
+}
+
+int
+ble_hs_adv_set_flat(uint8_t type, int data_len, void *data,
+                    uint8_t *dst, uint8_t *dst_len, uint8_t max_len)
+{
+    int rc;
+
+    rc = ble_hs_adv_set_hdr(type, data_len, max_len, dst, dst_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    memcpy(dst + *dst_len, data, data_len);
+    *dst_len += data_len;
+
+    return 0;
+}
+
+static int
+ble_hs_adv_set_array16(uint8_t type, uint8_t num_elems, uint16_t *elems,
+                       uint8_t *dst, uint8_t *dst_len, uint8_t max_len)
+{
+    int rc;
+    int i;
+
+    rc = ble_hs_adv_set_hdr(type, num_elems * sizeof *elems, max_len, dst,
+                            dst_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    for (i = 0; i < num_elems; i++) {
+        htole16(dst + *dst_len, elems[i]);
+        *dst_len += sizeof elems[i];
+    }
+
+    return 0;
+}
+
+static int
+ble_hs_adv_set_array32(uint8_t type, uint8_t num_elems, uint32_t *elems,
+                       uint8_t *dst, uint8_t *dst_len, uint8_t max_len)
+{
+    int rc;
+    int i;
+
+    rc = ble_hs_adv_set_hdr(type, num_elems * sizeof *elems, max_len, dst,
+                            dst_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    for (i = 0; i < num_elems; i++) {
+        htole32(dst + *dst_len, elems[i]);
+        *dst_len += sizeof elems[i];
+    }
 
     return 0;
 }
@@ -54,6 +110,56 @@ ble_hs_adv_set_fields(struct ble_hs_adv_fields *adv_fields,
 
     *dst_len = 0;
 
+    /*** 0x01 - Flags (written automatically by GAP). */
+
+    /*** 0x02,0x03 - 16-bit service class UUIDs. */
+    if (adv_fields->uuids16 != NULL && adv_fields->num_uuids16) {
+        if (adv_fields->uuids16_is_complete) {
+            type = BLE_HS_ADV_TYPE_COMP_UUIDS16;
+        } else {
+            type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
+        }
+
+        rc = ble_hs_adv_set_array16(type, adv_fields->num_uuids16,
+                                    adv_fields->uuids16, dst, dst_len,
+                                    max_len);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    /*** 0x04,0x05 - 32-bit service class UUIDs. */
+    if (adv_fields->uuids32 != NULL && adv_fields->num_uuids32) {
+        if (adv_fields->uuids32_is_complete) {
+            type = BLE_HS_ADV_TYPE_COMP_UUIDS32;
+        } else {
+            type = BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
+        }
+
+        rc = ble_hs_adv_set_array32(type, adv_fields->num_uuids32,
+                                    adv_fields->uuids32, dst, dst_len,
+                                    max_len);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    /*** 0x06,0x07 - 128-bit service class UUIDs. */
+    if (adv_fields->uuids128 != NULL && adv_fields->num_uuids128 > 0) {
+        if (adv_fields->uuids128_is_complete) {
+            type = BLE_HS_ADV_TYPE_COMP_UUIDS128;
+        } else {
+            type = BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
+        }
+
+        rc = ble_hs_adv_set_flat(type, adv_fields->num_uuids128 * 16,
+                                 adv_fields->uuids128, dst, dst_len, max_len);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    /*** 0x08,0x09 - Local name. */
     if (adv_fields->name != NULL && adv_fields->name_len > 0) {
         if (adv_fields->name_is_complete) {
             type = BLE_HS_ADV_TYPE_COMP_NAME;
@@ -61,8 +167,19 @@ ble_hs_adv_set_fields(struct ble_hs_adv_fields *adv_fields,
             type = BLE_HS_ADV_TYPE_INCOMP_NAME;
         }
 
-        rc = ble_hs_adv_set_one_field(type, adv_fields->name_len,
-                                      adv_fields->name, dst, dst_len, max_len);
+        rc = ble_hs_adv_set_flat(type, adv_fields->name_len, adv_fields->name,
+                                 dst, dst_len, max_len);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    /*** 0x0a - Tx power level (written automatically by GAP). */
+
+    /*** 0x1c - LE role. */
+    if (adv_fields->le_role_is_present) {
+        rc = ble_hs_adv_set_flat(BLE_HS_ADV_TYPE_LE_ROLE, 1,
+                                 &adv_fields->le_role, dst, dst_len, max_len);
         if (rc != 0) {
             return rc;
         }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/57949307/net/nimble/host/src/ble_hs_adv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_adv.h b/net/nimble/host/src/ble_hs_adv.h
index c9df5b1..c54632f 100644
--- a/net/nimble/host/src/ble_hs_adv.h
+++ b/net/nimble/host/src/ble_hs_adv.h
@@ -30,23 +30,24 @@ struct ble_hs_adv {
 };
 
 #define BLE_HS_ADV_TYPE_FLAGS                   0x01
-#define BLE_HS_ADV_TYPE_INCOMP_16BIT_UUIDS      0x02
-#define BLE_HS_ADV_TYPE_COMP_16BIT_UUIDS        0x03
-#define BLE_HS_ADV_TYPE_INCOMP_32BIT_UUIDS      0x04
-#define BLE_HS_ADV_TYPE_COMP_32BIT_UUIDS        0x05
-#define BLE_HS_ADV_TYPE_INCOMP_128BIT_UUIDS     0x06
-#define BLE_HS_ADV_TYPE_COMP_128BIT_UUIDS       0x07
+#define BLE_HS_ADV_TYPE_INCOMP_UUIDS16          0x02
+#define BLE_HS_ADV_TYPE_COMP_UUIDS16            0x03
+#define BLE_HS_ADV_TYPE_INCOMP_UUIDS32          0x04
+#define BLE_HS_ADV_TYPE_COMP_UUIDS32            0x05
+#define BLE_HS_ADV_TYPE_INCOMP_UUIDS128         0x06
+#define BLE_HS_ADV_TYPE_COMP_UUIDS128           0x07
 #define BLE_HS_ADV_TYPE_INCOMP_NAME             0x08
 #define BLE_HS_ADV_TYPE_COMP_NAME               0x09
 #define BLE_HS_ADV_TYPE_TX_PWR_LEVEL            0x0a
 #define BLE_HS_ADV_TYPE_DEVICE_CLASS            0x0b
+#define BLE_HS_ADV_TYPE_LE_ROLE                 0x1c
 
 #define BLE_HS_ADV_FLAGS_LEN                    1
 #define BLE_HS_ADV_F_DISC_LTD                   0x01
 #define BLE_HS_ADV_F_DISC_GEN                   0x02
 
-int ble_hs_adv_set_one_field(uint8_t type, uint8_t data_len, void *data,
-                             uint8_t *dst, uint8_t *dst_len, uint8_t max_len);
+int ble_hs_adv_set_flat(uint8_t type, int data_len, void *data,
+                        uint8_t *dst, uint8_t *dst_len, uint8_t max_len);
 int ble_hs_adv_set_fields(struct ble_hs_adv_fields *adv_fields,
                           uint8_t *dst, uint8_t *dst_len, uint8_t max_len);
 int ble_hs_adv_parse_fields(struct ble_hs_adv_fields *adv_fields, uint8_t *src,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/57949307/net/nimble/host/src/test/ble_hs_adv_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_adv_test.c b/net/nimble/host/src/test/ble_hs_adv_test.c
index 81f1c15..0c03eca 100644
--- a/net/nimble/host/src/test/ble_hs_adv_test.c
+++ b/net/nimble/host/src/test/ble_hs_adv_test.c
@@ -171,12 +171,165 @@ TEST_CASE(ble_hs_adv_test_case_flags)
 TEST_CASE(ble_hs_adv_test_case_user)
 {
     struct ble_hs_adv_fields fields;
+    int rc;
+
+    /*** Complete 16-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids16 = (uint16_t[]) { 0x0001, 0x1234, 0x54ab };
+    fields.num_uuids16 = 3;
+    fields.uuids16_is_complete = 1;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_COMP_UUIDS16,
+                .val = (uint8_t[]) { 0x01, 0x00, 0x34, 0x12, 0xab, 0x54 },
+                .val_len = 6,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
+
+    /*** Incomplete 16-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids16 = (uint16_t[]) { 0x0001, 0x1234, 0x54ab };
+    fields.num_uuids16 = 3;
+    fields.uuids16_is_complete = 0;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16,
+                .val = (uint8_t[]) { 0x01, 0x00, 0x34, 0x12, 0xab, 0x54 },
+                .val_len = 6,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
+
+    /*** Complete 32-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids32 = (uint32_t[]) { 0x12345678, 0xabacadae };
+    fields.num_uuids32 = 2;
+    fields.uuids32_is_complete = 1;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_COMP_UUIDS32,
+                .val = (uint8_t[]) { 0x78,0x56,0x34,0x12,0xae,0xad,0xac,0xab },
+                .val_len = 8,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
+
+    /*** Incomplete 32-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids32 = (uint32_t[]) { 0x12345678, 0xabacadae };
+    fields.num_uuids32 = 2;
+    fields.uuids32_is_complete = 0;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_INCOMP_UUIDS32,
+                .val = (uint8_t[]) { 0x78,0x56,0x34,0x12,0xae,0xad,0xac,0xab },
+                .val_len = 8,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
+
+    /*** Complete 128-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids128 = (uint8_t[]) {
+        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+    };
+    fields.num_uuids128 = 1;
+    fields.uuids128_is_complete = 1;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_COMP_UUIDS128,
+                .val = (uint8_t[]) {
+                    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+                    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+                },
+                .val_len = 16,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
+
+    /*** Incomplete 128-bit service class UUIDs. */
+    memset(&fields, 0, sizeof fields);
+    fields.uuids128 = (uint8_t[]) {
+        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+    };
+    fields.num_uuids128 = 1;
+    fields.uuids128_is_complete = 0;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_INCOMP_UUIDS128,
+                .val = (uint8_t[]) {
+                    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+                    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+                },
+                .val_len = 16,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
 
     /*** Complete name. */
+    memset(&fields, 0, sizeof fields);
     fields.name = (uint8_t *)"myname";
     fields.name_len = 6;
     fields.name_is_complete = 1;
-    ble_gap_conn_set_adv_fields(&fields);
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
 
     ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
         (struct ble_hs_adv_test_field[]) {
@@ -194,8 +347,12 @@ TEST_CASE(ble_hs_adv_test_case_user)
         });
 
     /*** Incomplete name. */
+    memset(&fields, 0, sizeof fields);
+    fields.name = (uint8_t *)"myname";
+    fields.name_len = 6;
     fields.name_is_complete = 0;
-    ble_gap_conn_set_adv_fields(&fields);
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
 
     ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
         (struct ble_hs_adv_test_field[]) {
@@ -211,6 +368,28 @@ TEST_CASE(ble_hs_adv_test_case_user)
             },
             { 0 },
         });
+
+    /*** LE role. */
+    memset(&fields, 0, sizeof fields);
+    fields.le_role = BLE_HS_ADV_LE_ROLE_BOTH_PERIPH_PREF;
+    fields.le_role_is_present = 1;
+    rc = ble_gap_conn_set_adv_fields(&fields);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_adv_test_misc_tx_and_verify_data(BLE_GAP_DISC_MODE_NON,
+        (struct ble_hs_adv_test_field[]) {
+            {
+                .type = BLE_HS_ADV_TYPE_LE_ROLE,
+                .val = (uint8_t[]) { BLE_HS_ADV_LE_ROLE_BOTH_PERIPH_PREF },
+                .val_len = 1,
+            },
+            {
+                .type = BLE_HS_ADV_TYPE_TX_PWR_LEVEL,
+                .val = (uint8_t[]){ 0x00 },
+                .val_len = 1,
+            },
+            { 0 },
+        });
 }
 
 TEST_SUITE(ble_hs_adv_test_suite)


[2/2] incubator-mynewt-larva git commit: Error code overhaul!

Posted by cc...@apache.org.
Error code overhaul!


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

Branch: refs/heads/master
Commit: 14f9937e2a0aabbe56b4750495ccb013163fa706
Parents: 5794930
Author: Christopher Collins <cc...@gmail.com>
Authored: Tue Dec 22 16:46:15 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Tue Dec 22 18:04:28 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_att.h          |   2 +
 net/nimble/host/include/host/ble_gatt.h         |  13 +-
 net/nimble/host/include/host/ble_hs.h           |  17 +-
 net/nimble/host/src/ble_att_clt.c               |  63 +--
 net/nimble/host/src/ble_att_svr.c               | 564 ++++++++++++-------
 net/nimble/host/src/ble_gatt.c                  | 159 +++---
 net/nimble/host/src/ble_hs_cfg.c                |  16 +
 net/nimble/host/src/ble_hs_misc.c               |  16 +
 net/nimble/host/src/test/ble_gatt_conn_test.c   |  34 +-
 net/nimble/host/src/test/ble_gatt_disc_c_test.c |  10 +-
 net/nimble/host/src/test/ble_gatt_disc_s_test.c |  15 +-
 net/nimble/host/src/test/ble_gatt_read_test.c   |  71 ++-
 net/nimble/host/src/test/ble_gatt_write_test.c  |   8 +-
 net/nimble/host/src/test/ble_hs_test_util.c     |   4 +-
 net/nimble/host/src/test/ble_hs_test_util.h     |   2 +-
 project/hostctlrtest/src/main.c                 |  33 +-
 16 files changed, 623 insertions(+), 404 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/include/host/ble_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_att.h b/net/nimble/host/include/host/ble_att.h
index 0b611da..e4c8112 100644
--- a/net/nimble/host/include/host/ble_att.h
+++ b/net/nimble/host/include/host/ble_att.h
@@ -17,6 +17,8 @@
 #ifndef H_BLE_ATT_
 #define H_BLE_ATT_
 
+#include "os/queue.h"
+
 #define BLE_ATT_UUID_PRIMARY_SERVICE        0x2800
 #define BLE_ATT_UUID_SECONDARY_SERVICE      0x2801
 #define BLE_ATT_UUID_INCLUDE                0x2802

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 b9c9eb3..04996e4 100644
--- a/net/nimble/host/include/host/ble_gatt.h
+++ b/net/nimble/host/include/host/ble_gatt.h
@@ -41,17 +41,14 @@ struct ble_gatt_chr {
     uint8_t uuid128[16];
 };
 
-typedef int ble_gatt_disc_service_fn(uint16_t conn_handle,
-                                     uint8_t ble_hs_status, uint8_t att_status,
+typedef int ble_gatt_disc_service_fn(uint16_t conn_handle, int status,
                                      struct ble_gatt_service *service,
                                      void *arg);
-typedef int ble_gatt_attr_fn(uint16_t conn_handle, uint8_t ble_hs_status,
-                             uint8_t att_status, struct ble_gatt_attr *attr,
-                             void *arg);
+typedef int ble_gatt_attr_fn(uint16_t conn_handle, int status,
+                             struct ble_gatt_attr *attr, void *arg);
 
-typedef int ble_gatt_chr_fn(uint16_t conn_handle, uint8_t ble_hs_status,
-                            uint8_t att_status, struct ble_gatt_chr *chr,
-                            void *arg);
+typedef int ble_gatt_chr_fn(uint16_t conn_handle, int status,
+                            struct ble_gatt_chr *chr, void *arg);
 
 int ble_gatt_disc_all_services(uint16_t conn_handle,
                                ble_gatt_disc_service_fn *cb,

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/include/host/ble_hs.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_hs.h b/net/nimble/host/include/host/ble_hs.h
index c7cb09c..24d9b9a 100644
--- a/net/nimble/host/include/host/ble_hs.h
+++ b/net/nimble/host/include/host/ble_hs.h
@@ -18,6 +18,7 @@
 #define H_BLE_HS_
 
 #include <inttypes.h>
+#include "host/ble_att.h"
 
 #define BLE_HS_EAGAIN                   1
 #define BLE_HS_EALREADY                 2
@@ -27,12 +28,26 @@
 #define BLE_HS_ENOMEM                   6
 #define BLE_HS_ENOTCONN                 7
 #define BLE_HS_ENOTSUP                  8
-#define BLE_HS_EATT                     9
+#define BLE_HS_EAPP                     9
 #define BLE_HS_EBADDATA                 10
 #define BLE_HS_EOS                      11
 #define BLE_HS_ECONGESTED               12
 #define BLE_HS_ECONTROLLER              13
 
+#define BLE_HS_ERR_ATT_BASE             0x100   /* 256 */
+#define ATT_ERR(att_code)               (BLE_HS_ERR_ATT_BASE + (att_code))
+
+#define BLE_HS_EATT_INVALID_HANDLE      ATT_ERR(BLE_ATT_ERR_INVALID_HANDLE)
+#define BLE_HS_EATT_INVALID_PDU         ATT_ERR(BLE_ATT_ERR_INVALID_PDU)
+#define BLE_HS_EATT_REQ_NOT_SUPPORTED   ATT_ERR(BLE_ATT_ERR_REQ_NOT_SUPPORTED)
+#define BLE_HS_EATT_INVALID_OFFSET      ATT_ERR(BLE_ATT_ERR_INVALID_OFFSET)
+#define BLE_HS_EATT_PREPARE_QUEUE_FULL  ATT_ERR(BLE_ATT_ERR_PREPARE_QUEUE_FULL)
+#define BLE_HS_EATT_ATTR_NOT_FOUND      ATT_ERR(BLE_ATT_ERR_ATTR_NOT_FOUND)
+#define BLE_HS_EATT_ATTR_VALUE_LEN      ATT_ERR(BLE_ATT_ERR_ATTR_LEN)
+#define BLE_HS_EATT_UNLIKELY            ATT_ERR(BLE_ATT_ERR_UNLIKELY)
+#define BLE_HS_EATT_UNSUPPORTED_GROUP   ATT_ERR(BLE_ATT_ERR_UNSUPPORTED_GROUP)
+#define BLE_HS_EATT_INSUFFICIENT_RES    ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_RES)
+
 struct ble_hs_cfg {
     uint16_t max_outstanding_pkts_per_conn;
     uint8_t max_connections;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 797b2e0..ad93428 100644
--- a/net/nimble/host/src/ble_att_clt.c
+++ b/net/nimble/host/src/ble_att_clt.c
@@ -109,9 +109,7 @@ ble_att_clt_tx_mtu(struct ble_hs_conn *conn, struct ble_att_mtu_cmd *req)
     }
 
     rc = ble_att_mtu_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
@@ -139,9 +137,7 @@ ble_att_clt_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     }
 
     rc = ble_att_mtu_cmd_parse((*om)->om_data, (*om)->om_len, &rsp);
-    if (rc != 0) {
-        return rc;
-    }
+    assert(rc == 0);
 
     ble_att_set_peer_mtu(chan, rsp.bamc_mtu);
 
@@ -167,16 +163,13 @@ ble_att_clt_tx_find_info(struct ble_hs_conn *conn,
         goto err;
     }
 
-    rc = ble_att_clt_prep_req(conn, &chan, &txom,
-                                 BLE_ATT_FIND_INFO_REQ_SZ);
+    rc = ble_att_clt_prep_req(conn, &chan, &txom, BLE_ATT_FIND_INFO_REQ_SZ);
     if (rc != 0) {
         goto err;
     }
 
     rc = ble_att_find_info_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
@@ -208,15 +201,15 @@ ble_att_clt_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         return BLE_HS_ENOMEM;
     }
 
-    rc = ble_att_find_info_rsp_parse((*om)->om_data, (*om)->om_len, &rsp);
-    if (rc != 0) {
-        return rc;
-    }
+    /* Double indirection is unwieldy; use rxom for remainder of function. */
     rxom = *om;
 
+    rc = ble_att_find_info_rsp_parse(rxom->om_data, rxom->om_len, &rsp);
+    assert(rc == 0);
+
     handle_id = 0;
     off = BLE_ATT_FIND_INFO_RSP_BASE_SZ;
-    while (off < OS_MBUF_PKTHDR(rxom)->omp_len) {
+    while (off < OS_MBUF_PKTLEN(rxom)) {
         rc = os_mbuf_copydata(rxom, off, 2, &handle_id);
         if (rc != 0) {
             return BLE_HS_EINVAL;
@@ -250,6 +243,8 @@ ble_att_clt_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         default:
             return BLE_HS_EINVAL;
         }
+
+        /* XXX: Notify GATT of handle-uuid pair. */
     }
 
     return 0;
@@ -280,9 +275,7 @@ ble_att_clt_tx_read_type(struct ble_hs_conn *conn,
     }
 
     rc = ble_att_read_type_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_hs_uuid_append(txom, uuid128);
     if (rc != 0) {
@@ -338,9 +331,7 @@ ble_att_clt_rx_read_type(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     }
 
     rc = ble_att_read_type_rsp_parse((*rxom)->om_data, (*rxom)->om_len, &rsp);
-    if (rc != 0) {
-        goto done;
-    }
+    assert(rc == 0);
 
     /* Strip the base from the front of the response. */
     os_mbuf_adj(*rxom, BLE_ATT_READ_TYPE_RSP_BASE_SZ);
@@ -385,9 +376,7 @@ ble_att_clt_tx_read(struct ble_hs_conn *conn, struct ble_att_read_req *req)
     }
 
     rc = ble_att_read_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
@@ -421,7 +410,7 @@ ble_att_clt_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     /* Pass the Attribute Value field to the GATT. */
     *rxom = os_mbuf_pullup(*rxom, OS_MBUF_PKTLEN(*rxom));
     if (*rxom == NULL) {
-        rc = BLE_HS_EMSGSIZE;
+        rc = BLE_HS_EBADDATA;
         goto done;
     }
 
@@ -460,9 +449,7 @@ ble_att_clt_tx_read_group_type(struct ble_hs_conn *conn,
     }
 
     rc = ble_att_read_group_type_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_hs_uuid_append(txom, uuid128);
     if (rc != 0) {
@@ -520,14 +507,12 @@ ble_att_clt_rx_read_group_type(struct ble_hs_conn *conn,
 
     rc = ble_att_read_group_type_rsp_parse((*rxom)->om_data, (*rxom)->om_len,
                                            &rsp);
-    if (rc != 0) {
-        goto done;
-    }
+    assert(rc == 0);
 
     /* Strip the base from the front of the response. */
     os_mbuf_adj(*rxom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
 
-    /* Parse the Attribute Data List field, passing each entry to the GATT. */
+    /* Parse the Attribute Data List field, passing each entry to GATT. */
     while (OS_MBUF_PKTLEN(*rxom) > 0) {
         rc = ble_att_clt_parse_group_attribute_data(rxom, rsp.bagp_length,
                                                     &adata);
@@ -571,9 +556,7 @@ ble_att_clt_tx_find_type_value(struct ble_hs_conn *conn,
     }
 
     rc = ble_att_find_type_value_req_write(txom->om_data, txom->om_len, req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = os_mbuf_append(txom, attribute_value, value_len);
     if (rc != 0) {
@@ -625,9 +608,7 @@ ble_att_clt_rx_find_type_value(struct ble_hs_conn *conn,
      */
     os_mbuf_adj(*rxom, BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ);
 
-    /* Parse the Handles Information List field, passing each entry to the
-     * GATT.
-     */
+    /* Parse the Handles Information List field, passing each entry to GATT. */
     rc = 0;
     while (OS_MBUF_PKTLEN(*rxom) > 0) {
         rc = ble_att_clt_parse_handles_info(rxom, &adata);
@@ -669,9 +650,7 @@ ble_att_clt_tx_write_req_or_cmd(struct ble_hs_conn *conn,
     } else {
         rc = ble_att_write_cmd_write(txom->om_data, txom->om_len, req);
     }
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     mtu = ble_l2cap_chan_mtu(chan);
     extra_len = BLE_ATT_WRITE_REQ_BASE_SZ + value_len - mtu;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 f4f0911..0d46125 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -292,13 +292,13 @@ ble_att_svr_tx_error_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     dst = os_mbuf_extend(txom, BLE_ATT_ERROR_RSP_SZ);
     if (dst == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -312,7 +312,6 @@ ble_att_svr_tx_error_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -325,25 +324,29 @@ err:
 
 static int
 ble_att_svr_tx_mtu_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
-                       uint8_t op, uint16_t mtu)
+                       uint8_t op, uint16_t mtu, uint8_t *att_err)
 {
     struct ble_att_mtu_cmd cmd;
     struct os_mbuf *txom;
     void *dst;
     int rc;
 
+    *att_err = 0; /* Silence unnecessary warning. */
+
     assert(op == BLE_ATT_OP_MTU_REQ || op == BLE_ATT_OP_MTU_RSP);
     assert(mtu >= BLE_ATT_MTU_DFLT);
 
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     dst = os_mbuf_extend(txom, BLE_ATT_MTU_CMD_SZ);
     if (dst == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -355,7 +358,7 @@ ble_att_svr_tx_mtu_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -372,11 +375,14 @@ ble_att_svr_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                    struct os_mbuf **om)
 {
     struct ble_att_mtu_cmd cmd;
+    uint8_t att_err;
     int rc;
 
     *om = os_mbuf_pullup(*om, BLE_ATT_MTU_CMD_SZ);
     if (*om == NULL) {
-        return BLE_HS_ENOMEM;
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
+        goto err;
     }
 
     rc = ble_att_mtu_cmd_parse((*om)->om_data, (*om)->om_len, &cmd);
@@ -384,12 +390,16 @@ ble_att_svr_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
     ble_att_set_peer_mtu(chan, cmd.bamc_mtu);
     rc = ble_att_svr_tx_mtu_rsp(conn, chan, BLE_ATT_OP_MTU_RSP,
-                                chan->blc_my_mtu);
+                                chan->blc_my_mtu, &att_err);
     if (rc != 0) {
-        return rc;
+        goto err;
     }
 
     return 0;
+
+err:
+    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_MTU_REQ, 0, att_err);
+    return rc;
 }
 
 /**
@@ -406,7 +416,7 @@ ble_att_svr_rx_mtu(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
  *                                     o BLE_ATT_FIND_INFO_RSP_FORMAT_16BIT
  *                                     o BLE_ATT_FIND_INFO_RSP_FORMAT_128BIT
  *
- * @return                      0 on success; an ATT error code on failure.
+ * @return                      0 on success; nonzero on failure.
  */
 static int
 ble_att_svr_fill_info(struct ble_att_find_info_req *req, struct os_mbuf *om,
@@ -511,43 +521,19 @@ done:
     }
 }
 
-int
-ble_att_svr_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
-                         struct os_mbuf **rxom)
+static int
+ble_att_svr_tx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                         struct ble_att_find_info_req *req, uint8_t *att_err)
 {
-    struct ble_att_find_info_req req;
     struct ble_att_find_info_rsp rsp;
     struct os_mbuf *txom;
     void *buf;
     int rc;
 
-    txom = NULL;
-
-    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_MTU_CMD_SZ);
-    if (*rxom == NULL) {
-        return BLE_HS_ENOMEM;
-    }
-
-    rc = ble_att_find_info_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
-    if (rc != 0) {
-        req.bafq_start_handle = 0;
-        rc = BLE_ATT_ERR_INVALID_PDU;
-        goto err;
-    }
-
-    /* Tx error response if start handle is greater than end handle or is equal
-     * to 0 (Vol. 3, Part F, 3.4.3.1).
-     */
-    if (req.bafq_start_handle > req.bafq_end_handle ||
-        req.bafq_start_handle == 0) {
-
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
-        goto err;
-    }
-
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -556,7 +542,8 @@ ble_att_svr_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
      */
     buf = os_mbuf_extend(txom, BLE_ATT_FIND_INFO_RSP_BASE_SZ);
     if (buf == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -566,16 +553,18 @@ ble_att_svr_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     /* Write the variable length Information Data field, populating the format
      * field as appropriate.
      */
-    rc = ble_att_svr_fill_info(&req, txom, ble_l2cap_chan_mtu(chan),
+    rc = ble_att_svr_fill_info(req, txom, ble_l2cap_chan_mtu(chan),
                                txom->om_data + 1);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_ATTR_NOT_FOUND;
+        *att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
+        rc = BLE_HS_ENOENT;
         goto err;
     }
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -583,9 +572,52 @@ ble_att_svr_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
 err:
     os_mbuf_free_chain(txom);
-    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_FIND_INFO_REQ,
-                             req.bafq_start_handle, rc);
+    return rc;
+}
 
+int
+ble_att_svr_rx_find_info(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                         struct os_mbuf **rxom)
+{
+    struct ble_att_find_info_req req;
+    uint16_t err_handle;
+    uint8_t att_err;
+    int rc;
+
+    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_MTU_CMD_SZ);
+    if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = ble_att_find_info_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
+    assert(rc == 0);
+
+    /* Tx error response if start handle is greater than end handle or is equal
+     * to 0 (Vol. 3, Part F, 3.4.3.1).
+     */
+    if (req.bafq_start_handle > req.bafq_end_handle ||
+        req.bafq_start_handle == 0) {
+
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.bafq_start_handle;
+        rc = BLE_HS_EBADDATA;
+        goto err;
+    }
+
+    rc = ble_att_svr_tx_find_info(conn, chan, &req, &att_err);
+    if (rc != 0) {
+        err_handle = req.bafq_start_handle;
+        goto err;
+    }
+
+    return 0;
+
+err:
+    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_FIND_INFO_REQ,
+                             err_handle, att_err);
     return rc;
 }
 
@@ -710,7 +742,9 @@ ble_att_svr_fill_type_value_match(struct os_mbuf *om, uint16_t *first,
  *                                  written.
  * @param mtu                   The ATT L2CAP channel MTU.
  *
- * @return                      0 on success; an ATT error code on failure.
+ * @return                      0 on success;
+ *                              BLE_HS_ENOENT if attribute not found;
+ *                              BLE_HS_EAPP on other error.
  */
 static int
 ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
@@ -751,7 +785,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
             if (uuid16 == req->bavq_attr_type) {
                 rc = ha->ha_fn(ha, BLE_ATT_OP_READ_REQ, &arg);
                 if (rc != 0) {
-                    rc = BLE_ATT_ERR_UNLIKELY;
+                    rc = BLE_HS_EAPP;
                     goto done;
                 }
                 rc = os_mbuf_memcmp(rxom,
@@ -776,7 +810,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
             goto done;
         }
         if (rc != BLE_HS_EAGAIN) {
-            rc = BLE_ATT_ERR_UNLIKELY;
+            rc = BLE_HS_EAPP;
             goto done;
         }
     }
@@ -788,7 +822,7 @@ ble_att_svr_fill_type_value(struct ble_att_find_type_value_req *req,
     if (rc == BLE_HS_EAGAIN) {
         rc = 0;
     } else if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        rc = BLE_HS_EAPP;
     }
 
 done:
@@ -797,68 +831,64 @@ done:
     any_entries = OS_MBUF_PKTHDR(txom)->omp_len >
                   BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ;
     if (rc == 0 && !any_entries) {
-        return BLE_ATT_ERR_ATTR_NOT_FOUND;
+        return BLE_HS_ENOENT;
     } else {
         return rc;
     }
 }
 
-int
-ble_att_svr_rx_find_type_value(struct ble_hs_conn *conn,
+static int
+ble_att_svr_tx_find_type_value(struct ble_hs_conn *conn,
                                struct ble_l2cap_chan *chan,
-                               struct os_mbuf **rxom)
+                               struct ble_att_find_type_value_req *req,
+                               struct os_mbuf *rxom,
+                               uint8_t *att_err)
 {
-    struct ble_att_find_type_value_req req;
     struct os_mbuf *txom;
     uint8_t *buf;
     int rc;
 
-    txom = NULL;
-
-    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_MTU_CMD_SZ);
-    if (*rxom == NULL) {
-        return BLE_HS_ENOMEM;
-    }
-
-    rc = ble_att_find_type_value_req_parse((*rxom)->om_data, (*rxom)->om_len,
-                                           &req);
-    assert(rc == 0);
-
-    /* Tx error response if start handle is greater than end handle or is equal
-     * to 0 (Vol. 3, Part F, 3.4.3.3).
-     */
-    if (req.bavq_start_handle > req.bavq_end_handle ||
-        req.bavq_start_handle == 0) {
-
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
-        goto err;
-    }
-
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     /* Write the response base at the start of the buffer. */
     buf = os_mbuf_extend(txom, BLE_ATT_FIND_TYPE_VALUE_RSP_BASE_SZ);
     if (buf == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
     buf[0] = BLE_ATT_OP_FIND_TYPE_VALUE_RSP;
 
     /* Write the variable length Information Data field. */
-    rc = ble_att_svr_fill_type_value(&req, *rxom, txom,
+    rc = ble_att_svr_fill_type_value(req, rxom, txom,
                                      ble_l2cap_chan_mtu(chan));
-    if (rc != 0) {
+    switch (rc) {
+    case 0:
+        break;
+
+    case BLE_HS_ENOENT:
+        *att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
+        goto err;
+
+    case BLE_HS_EAPP:
+        *att_err = BLE_ATT_ERR_UNLIKELY;
+        goto err;
+
+    default:
+        assert(0);
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -866,8 +896,54 @@ ble_att_svr_rx_find_type_value(struct ble_hs_conn *conn,
 
 err:
     os_mbuf_free_chain(txom);
+    return rc;
+}
+
+int
+ble_att_svr_rx_find_type_value(struct ble_hs_conn *conn,
+                               struct ble_l2cap_chan *chan,
+                               struct os_mbuf **rxom)
+{
+    struct ble_att_find_type_value_req req;
+    uint16_t err_handle;
+    uint8_t att_err;
+    int rc;
+
+    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_MTU_CMD_SZ);
+    if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = ble_att_find_type_value_req_parse((*rxom)->om_data, (*rxom)->om_len,
+                                           &req);
+    assert(rc == 0);
+
+    /* Tx error response if start handle is greater than end handle or is equal
+     * to 0 (Vol. 3, Part F, 3.4.3.3).
+     */
+    if (req.bavq_start_handle > req.bavq_end_handle ||
+        req.bavq_start_handle == 0) {
+
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.bavq_start_handle;
+        rc = BLE_HS_EBADDATA;
+        goto err;
+    }
+
+    rc = ble_att_svr_tx_find_type_value(conn, chan, &req, *rxom, &att_err);
+    if (rc != 0) {
+        err_handle = req.bavq_start_handle;
+        goto err;
+    }
+
+    return 0;
+
+err:
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_FIND_TYPE_VALUE_REQ,
-                             req.bavq_start_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 
@@ -875,7 +951,8 @@ static int
 ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
                              struct ble_l2cap_chan *chan,
                              struct ble_att_read_type_req *req,
-                             uint8_t *uuid128)
+                             uint8_t *uuid128, uint8_t *att_err,
+                             uint16_t *err_handle)
 {
     struct ble_att_read_type_rsp rsp;
     union ble_att_svr_handle_arg arg;
@@ -887,10 +964,17 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
     int attr_len;
     int rc;
 
+    *att_err = 0;    /* Silence unnecessary warning. */
+    *err_handle = 0; /* Silence unnecessary warning. */
+
+    prev_attr_len = 0;
+
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
-        goto err;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *err_handle = 0;
+        rc = BLE_HS_ENOMEM;
+        goto done;
     }
 
     /* Allocate space for the respose base, but don't fill in the fields.  They
@@ -898,20 +982,22 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
      */
     dptr = os_mbuf_extend(txom, BLE_ATT_READ_TYPE_RSP_BASE_SZ);
     if (dptr == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
-        goto err;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *err_handle = 0;
+        rc = BLE_HS_ENOMEM;
+        goto done;
     }
 
     /* Find all matching attributes, writing a record for each. */
-    prev_attr_len = 0;
     entry = NULL;
     while (1) {
         rc = ble_att_svr_find_by_uuid(uuid128, &entry);
         if (rc == BLE_HS_ENOENT) {
             break;
         } else if (rc != 0) {
-            rc = BLE_ATT_ERR_UNLIKELY;
-            goto err;
+            *att_err = BLE_ATT_ERR_UNLIKELY;
+            *err_handle = 0;
+            goto done;
         }
 
         if (entry->ha_handle_id > req->batq_end_handle) {
@@ -923,8 +1009,9 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
 
             rc = entry->ha_fn(entry, BLE_ATT_OP_READ_REQ, &arg);
             if (rc != 0) {
-                rc = BLE_ATT_ERR_UNLIKELY;
-                goto err;
+                *att_err = BLE_ATT_ERR_UNLIKELY;
+                *err_handle = entry->ha_handle_id;
+                goto done;
             }
 
             if (arg.aha_read.attr_len > ble_l2cap_chan_mtu(chan) - 4) {
@@ -946,8 +1033,10 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
 
             dptr = os_mbuf_extend(txom, 2 + attr_len);
             if (dptr == NULL) {
-                rc = BLE_ATT_ERR_INSUFFICIENT_RES;
-                goto err;
+                *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+                *err_handle = entry->ha_handle_id;
+                rc = BLE_HS_ENOMEM;
+                goto done;
             }
 
             htole16(dptr + 0, entry->ha_handle_id);
@@ -955,31 +1044,32 @@ ble_att_svr_tx_read_type_rsp(struct ble_hs_conn *conn,
         }
     }
 
-    if (prev_attr_len == 0) {
+done:
+    if (OS_MBUF_PKTLEN(txom) == 0) {
         /* No matching attributes. */
-        rc = BLE_ATT_ERR_ATTR_NOT_FOUND;
-        goto err;
-    }
+        if (*att_err == 0) {
+            *att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
+        }
+        rc = BLE_HS_ENOENT;
+    } else {
+        /* Send what we can, even if an error was encountered. */
+        *att_err = 0;
 
-    /* Fill the response base. */
-    rsp.batp_length = BLE_ATT_READ_TYPE_ADATA_BASE_SZ + prev_attr_len;
-    rc = ble_att_read_type_rsp_write(txom->om_data, txom->om_len, &rsp);
-    assert(rc == 0);
+        /* Fill the response base. */
+        rsp.batp_length = BLE_ATT_READ_TYPE_ADATA_BASE_SZ + prev_attr_len;
+        rc = ble_att_read_type_rsp_write(txom->om_data, txom->om_len, &rsp);
+        assert(rc == 0);
 
-    /* Transmit the response. */
-    rc = ble_l2cap_tx(conn, chan, txom);
-    txom = NULL;
-    if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
-        goto err;
+        /* Transmit the response. */
+        rc = ble_l2cap_tx(conn, chan, txom);
+        txom = NULL;
+        if (rc != 0) {
+            *att_err = BLE_ATT_ERR_UNLIKELY;
+            *err_handle = entry->ha_handle_id;
+        }
     }
 
-    return 0;
-
-err:
     os_mbuf_free_chain(txom);
-    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_READ_TYPE_REQ,
-                             req->batq_start_handle, rc);
     return rc;
 }
 
@@ -988,26 +1078,32 @@ ble_att_svr_rx_read_type(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                          struct os_mbuf **rxom)
 {
     struct ble_att_read_type_req req;
+    uint16_t err_handle;
     uint16_t uuid16;
     uint8_t uuid128[16];
+    uint8_t att_err;
     int rc;
 
     *rxom = os_mbuf_pullup(*rxom, OS_MBUF_PKTLEN(*rxom));
     if (*rxom == NULL) {
-        return BLE_HS_ENOMEM;
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
+        rc = BLE_HS_ENOMEM;
+        goto err;
     }
 
     rc = ble_att_read_type_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
-    if (rc != 0) {
-        return rc;
-    }
+    assert(rc == 0);
 
     switch ((*rxom)->om_len) {
     case BLE_ATT_READ_TYPE_REQ_SZ_16:
         uuid16 = le16toh((*rxom)->om_data + 5);
         rc = ble_hs_uuid_from_16bit(uuid16, uuid128);
         if (rc != 0) {
-            return rc;
+            att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
+            err_handle = 0;
+            rc = BLE_HS_EBADDATA;
+            goto err;
         }
         break;
 
@@ -1016,20 +1112,32 @@ ble_att_svr_rx_read_type(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         break;
 
     default:
-        return BLE_HS_EMSGSIZE;
+        att_err = BLE_ATT_ERR_INVALID_PDU;
+        err_handle = 0;
+        rc = BLE_HS_EMSGSIZE;
+        goto err;
     }
 
-    rc = ble_att_svr_tx_read_type_rsp(conn, chan, &req, uuid128);
+    rc = ble_att_svr_tx_read_type_rsp(conn, chan, &req, uuid128, &att_err,
+                                      &err_handle);
     if (rc != 0) {
-        return rc;
+        goto err;
     }
 
     return 0;
+
+err:
+    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_READ_TYPE_REQ,
+                             err_handle, att_err);
+    return rc;
 }
 
+/**
+ * @return                      0 on success; ATT error code on failure.
+ */
 static int
 ble_att_svr_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
-                        void *attr_data, int attr_len)
+                        void *attr_data, int attr_len, uint8_t *att_err)
 {
     struct os_mbuf *txom;
     uint16_t data_len;
@@ -1038,14 +1146,16 @@ ble_att_svr_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     op = BLE_ATT_OP_READ_RSP;
     rc = os_mbuf_append(txom, &op, 1);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -1058,13 +1168,14 @@ ble_att_svr_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
     rc = os_mbuf_append(txom, attr_data, data_len);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     rc = ble_l2cap_tx(conn, chan, txom);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -1082,40 +1193,54 @@ ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     union ble_att_svr_handle_arg arg;
     struct ble_att_svr_entry *entry;
     struct ble_att_read_req req;
+    uint16_t err_handle;
+    uint8_t att_err;
     int rc;
 
     *rxom = os_mbuf_pullup(*rxom, OS_MBUF_PKTLEN(*rxom));
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
         rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     rc = ble_att_read_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
     if (rc != 0) {
+        att_err = BLE_ATT_ERR_INVALID_PDU;
+        err_handle = 0;
+        rc = BLE_HS_EBADDATA;
         goto err;
     }
 
     entry = NULL;
     rc = ble_att_svr_find_by_handle(req.barq_handle, &entry);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.barq_handle;
+        rc = BLE_HS_ENOENT;
         goto err;
     }
 
     if (entry->ha_fn == NULL) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        att_err = BLE_ATT_ERR_UNLIKELY;
+        err_handle = req.barq_handle;
+        rc = BLE_HS_ENOTSUP;
         goto err;
     }
 
     rc = entry->ha_fn(entry, BLE_ATT_OP_READ_REQ, &arg);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        att_err = BLE_ATT_ERR_UNLIKELY;
+        err_handle = req.barq_handle;
+        rc = BLE_HS_ENOTSUP;
         goto err;
     }
 
     rc = ble_att_svr_tx_read_rsp(conn, chan, arg.aha_read.attr_data,
-                                 arg.aha_read.attr_len);
+                                 arg.aha_read.attr_len, &att_err);
     if (rc != 0) {
+        err_handle = req.barq_handle;
         goto err;
     }
 
@@ -1123,7 +1248,7 @@ ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 
 err:
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_READ_REQ,
-                             req.barq_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 
@@ -1203,11 +1328,15 @@ ble_att_svr_read_group_type_entry_write(struct os_mbuf *om, uint16_t mtu,
     return 0;
 }
 
+/**
+ * @return                      0 on success; BLE_HS error code on failure.
+ */
 static int
 ble_att_svr_tx_read_group_type(struct ble_hs_conn *conn,
                                struct ble_l2cap_chan *chan,
                                struct ble_att_read_group_type_req *req,
-                               uint8_t *group_uuid128, uint16_t *err_handle)
+                               uint8_t *group_uuid128, uint8_t *att_err,
+                               uint16_t *err_handle)
 {
     struct ble_att_read_group_type_rsp rsp;
     struct ble_att_svr_entry *entry;
@@ -1224,6 +1353,7 @@ ble_att_svr_tx_read_group_type(struct ble_hs_conn *conn,
     service_uuid16 = 0;
     end_group_handle = 0;
 
+    *att_err = 0;
     *err_handle = req->bagq_start_handle;
 
     txom = ble_att_get_pkthdr();
@@ -1235,7 +1365,8 @@ ble_att_svr_tx_read_group_type(struct ble_hs_conn *conn,
     /* Reserve space for the response base. */
     rsp_buf = os_mbuf_extend(txom, BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ);
     if (rsp_buf == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto done;
     }
 
@@ -1268,6 +1399,11 @@ ble_att_svr_tx_read_group_type(struct ble_hs_conn *conn,
                 end_group_handle = 0;
                 if (rc != 0) {
                     *err_handle = entry->ha_handle_id;
+                    if (rc == BLE_HS_ENOMEM) {
+                        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+                    } else {
+                        assert(rc == BLE_HS_EMSGSIZE);
+                    }
                     goto done;
                 }
             }
@@ -1281,7 +1417,8 @@ ble_att_svr_tx_read_group_type(struct ble_hs_conn *conn,
                                               service_uuid128);
                 if (rc != 0) {
                     *err_handle = entry->ha_handle_id;
-                    rc = BLE_ATT_ERR_UNLIKELY;
+                    *att_err = BLE_ATT_ERR_UNLIKELY;
+                    rc = BLE_HS_ENOTSUP;
                     goto done;
                 }
 
@@ -1335,10 +1472,14 @@ done:
                 txom, ble_l2cap_chan_mtu(chan),
                 start_group_handle, end_group_handle,
                 service_uuid16, service_uuid128);
+            if (rc == BLE_HS_ENOMEM) {
+                *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+            }
         }
 
         if (OS_MBUF_PKTLEN(txom) <= BLE_ATT_READ_GROUP_TYPE_RSP_BASE_SZ) {
-            rc = BLE_ATT_ERR_ATTR_NOT_FOUND;
+            *att_err = BLE_ATT_ERR_ATTR_NOT_FOUND;
+            rc = BLE_HS_ENOENT;
         }
     }
 
@@ -1362,14 +1503,15 @@ ble_att_svr_rx_read_group_type(struct ble_hs_conn *conn,
                                struct os_mbuf **rxom)
 {
     struct ble_att_read_group_type_req req;
-    uint16_t err_handle;
     uint8_t uuid128[16];
+    uint16_t err_handle;
+    uint8_t att_err;
     int rc;
 
-    err_handle = 0;
-
     *rxom = os_mbuf_pullup(*rxom, OS_MBUF_PKTLEN(*rxom));
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
         rc = BLE_HS_ENOMEM;
         goto err;
     }
@@ -1377,30 +1519,39 @@ ble_att_svr_rx_read_group_type(struct ble_hs_conn *conn,
     rc = ble_att_read_group_type_req_parse((*rxom)->om_data, (*rxom)->om_len,
                                            &req);
     if (rc != 0) {
+        att_err = BLE_ATT_ERR_INVALID_PDU;
+        err_handle = 0;
+        rc = BLE_HS_EBADDATA;
         goto err;
     }
-    err_handle = req.bagq_start_handle;
 
     if (req.bagq_start_handle > req.bagq_end_handle ||
         req.bagq_start_handle == 0) {
 
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.bagq_start_handle;
+        rc = BLE_HS_EBADDATA;
         goto err;
     }
 
     rc = ble_hs_uuid_extract(*rxom, BLE_ATT_READ_GROUP_TYPE_REQ_BASE_SZ,
                              uuid128);
     if (rc != 0) {
+        att_err = BLE_ATT_ERR_INVALID_PDU;
+        err_handle = req.bagq_start_handle;
+        rc = BLE_HS_EBADDATA;
         goto err;
     }
 
     if (!ble_att_svr_is_valid_group_type(uuid128)) {
-        rc = BLE_ATT_ERR_UNSUPPORTED_GROUP;
+        att_err = BLE_ATT_ERR_UNSUPPORTED_GROUP;
+        err_handle = req.bagq_start_handle;
+        rc = BLE_HS_ENOTSUP;
         goto err;
     }
 
     rc = ble_att_svr_tx_read_group_type(conn, chan, &req, uuid128,
-                                        &err_handle);
+                                        &att_err, &err_handle);
     if (rc != 0) {
         goto err;
     }
@@ -1409,12 +1560,13 @@ ble_att_svr_rx_read_group_type(struct ble_hs_conn *conn,
 
 err:
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_READ_GROUP_TYPE_REQ,
-                             err_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 
 static int
-ble_att_svr_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+ble_att_svr_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                         uint8_t *att_err)
 {
     struct os_mbuf *txom;
     uint8_t *dst;
@@ -1422,13 +1574,15 @@ ble_att_svr_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
 
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     dst = os_mbuf_extend(txom, BLE_ATT_WRITE_RSP_SZ);
     if (dst == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
@@ -1436,7 +1590,7 @@ ble_att_svr_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -1454,52 +1608,61 @@ ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
     union ble_att_svr_handle_arg arg;
     struct ble_att_svr_entry *entry;
     struct ble_att_write_req req;
+    uint16_t err_handle;
+    uint8_t att_err;
     int rc;
 
     *rxom = os_mbuf_pullup(*rxom, BLE_ATT_WRITE_REQ_BASE_SZ);
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
         rc = BLE_HS_ENOMEM;
-        goto send_err;
+        goto err;
     }
 
     rc = ble_att_write_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
-    if (rc != 0) {
-        goto send_err;
-    }
+    assert(rc == 0);
 
     os_mbuf_adj(*rxom, BLE_ATT_WRITE_REQ_BASE_SZ);
 
     entry = NULL;
     rc = ble_att_svr_find_by_handle(req.bawq_handle, &entry);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
-        goto send_err;
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.bawq_handle;
+        rc = BLE_HS_ENOENT;
+        goto err;
     }
 
     if (entry->ha_fn == NULL) {
-        rc = BLE_ATT_ERR_UNLIKELY;
-        goto send_err;
+        att_err = BLE_ATT_ERR_UNLIKELY;
+        err_handle = req.bawq_handle;
+        rc = BLE_HS_ENOTSUP;
+        goto err;
     }
 
     arg.aha_write.attr_data = ble_att_svr_flat_buf;
     arg.aha_write.attr_len = OS_MBUF_PKTLEN(*rxom);
     os_mbuf_copydata(*rxom, 0, arg.aha_write.attr_len,
                      arg.aha_write.attr_data);
-    rc = entry->ha_fn(entry, BLE_ATT_OP_WRITE_REQ, &arg);
-    if (rc != 0) {
-        goto send_err;
+    att_err = entry->ha_fn(entry, BLE_ATT_OP_WRITE_REQ, &arg);
+    if (att_err != 0) {
+        err_handle = req.bawq_handle;
+        rc = BLE_HS_EAPP;
+        goto err;
     }
 
-    rc = ble_att_svr_tx_write_rsp(conn, chan);
+    rc = ble_att_svr_tx_write_rsp(conn, chan, &att_err);
     if (rc != 0) {
-        goto send_err;
+        err_handle = req.bawq_handle;
+        goto err;
     }
 
     return 0;
 
-send_err:
+err:
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_WRITE_REQ,
-                             req.bawq_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 
@@ -1564,6 +1727,9 @@ ble_att_svr_prep_clear(struct ble_att_svr_conn *basc)
     }
 }
 
+/**
+ * @return                      0 on success; ATT error code on failure.
+ */
 static int
 ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
 {
@@ -1601,6 +1767,9 @@ ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
     return 0;
 }
 
+/**
+ * @return                      0 on success; ATT error code on failure.
+ */
 static int
 ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
 {
@@ -1611,6 +1780,8 @@ ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
     int buf_off;
     int rc;
 
+    *err_handle = 0; /* Silence unnecessary warning. */
+
     /* First, validate the contents of the prepare queue. */
     rc = ble_att_svr_prep_validate(basc, err_handle);
     if (rc != 0) {
@@ -1666,35 +1837,39 @@ ble_att_svr_rx_prep_write(struct ble_hs_conn *conn,
     struct ble_att_svr_entry *attr_entry;
     struct os_mbuf *srcom;
     struct os_mbuf *txom;
+    uint16_t err_handle;
+    uint8_t att_err;
     int rc;
 
     /* Initialize some values in case of early error. */
     prep_entry = NULL;
-    req.bapc_handle = 0;
 
     *rxom = os_mbuf_pullup(*rxom, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
         rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     rc = ble_att_prep_write_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     os_mbuf_adj(*rxom, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
 
     attr_entry = NULL;
     rc = ble_att_svr_find_by_handle(req.bapc_handle, &attr_entry);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_INVALID_HANDLE;
+        att_err = BLE_ATT_ERR_INVALID_HANDLE;
+        err_handle = req.bapc_handle;
         goto err;
     }
 
     prep_entry = ble_att_svr_prep_alloc();
     if (prep_entry == NULL) {
-        rc = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+        att_err = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+        err_handle = req.bapc_handle;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
     prep_entry->bape_handle = req.bapc_handle;
@@ -1714,7 +1889,8 @@ ble_att_svr_rx_prep_write(struct ble_hs_conn *conn,
         rc = os_mbuf_append(prep_entry->bape_value, srcom->om_data,
                             srcom->om_len);
         if (rc != 0) {
-            rc = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+            att_err = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+            err_handle = req.bapc_handle;
             goto err;
         }
     }
@@ -1724,19 +1900,21 @@ ble_att_svr_rx_prep_write(struct ble_hs_conn *conn,
      */
     *rxom = os_mbuf_prepend(*rxom, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = req.bapc_handle;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
     txom = *rxom;
 
     rc = ble_att_prep_write_rsp_write(txom->om_data,
                                       BLE_ATT_PREP_WRITE_CMD_BASE_SZ, &req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_l2cap_tx(conn, chan, txom);
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        att_err = BLE_ATT_ERR_UNLIKELY;
+        err_handle = req.bapc_handle;
         goto err;
     }
 
@@ -1760,13 +1938,16 @@ err:
     }
 
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_PREP_WRITE_REQ,
-                             req.bapc_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 
+/**
+ * @return                      0 on success; ATT error code on failure.
+ */
 static int
 ble_att_svr_tx_exec_write_rsp(struct ble_hs_conn *conn,
-                              struct ble_l2cap_chan *chan)
+                              struct ble_l2cap_chan *chan, uint8_t *att_err)
 {
     struct os_mbuf *txom;
     uint8_t *dst;
@@ -1774,25 +1955,25 @@ ble_att_svr_tx_exec_write_rsp(struct ble_hs_conn *conn,
 
     txom = ble_att_get_pkthdr();
     if (txom == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     dst = os_mbuf_extend(txom, BLE_ATT_EXEC_WRITE_RSP_SZ);
     if (dst == NULL) {
-        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        *att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     rc = ble_att_exec_write_rsp_write(dst, BLE_ATT_EXEC_WRITE_RSP_SZ);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     rc = ble_l2cap_tx(conn, chan, txom);
     txom = NULL;
     if (rc != 0) {
-        rc = BLE_ATT_ERR_UNLIKELY;
+        *att_err = BLE_ATT_ERR_UNLIKELY;
         goto err;
     }
 
@@ -1810,39 +1991,40 @@ ble_att_svr_rx_exec_write(struct ble_hs_conn *conn,
 {
     struct ble_att_exec_write_req req;
     uint16_t err_handle;
+    uint8_t att_err;
     int rc;
 
-    /* Initialize some values in case of early error. */
-    err_handle = 0;
-
     *rxom = os_mbuf_pullup(*rxom, BLE_ATT_EXEC_WRITE_REQ_SZ);
     if (*rxom == NULL) {
+        att_err = BLE_ATT_ERR_INSUFFICIENT_RES;
+        err_handle = 0;
         rc = BLE_HS_ENOMEM;
         goto err;
     }
 
     rc = ble_att_exec_write_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
-    if (rc != 0) {
-        goto err;
-    }
+    assert(rc == 0);
 
     if (req.baeq_flags & BLE_ATT_EXEC_WRITE_F_CONFIRM) {
         /* Perform attribute writes. */
-        rc = ble_att_svr_prep_write(&conn->bhc_att_svr, &err_handle);
+        att_err = ble_att_svr_prep_write(&conn->bhc_att_svr, &err_handle);
     } else {
-        rc = 0;
+        att_err = 0;
+        err_handle = 0;
     }
 
     /* Erase all prep entries. */
     ble_att_svr_prep_clear(&conn->bhc_att_svr);
 
-    if (rc != 0) {
+    if (att_err != 0) {
+        rc = BLE_HS_EAPP;
         goto err;
     }
 
     /* Send response. */
-    rc = ble_att_svr_tx_exec_write_rsp(conn, chan);
+    rc = ble_att_svr_tx_exec_write_rsp(conn, chan, &att_err);
     if (rc != 0) {
+        err_handle = 0;
         goto err;
     }
 
@@ -1850,7 +2032,7 @@ ble_att_svr_rx_exec_write(struct ble_hs_conn *conn,
 
 err:
     ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_EXEC_WRITE_REQ,
-                             err_handle, rc);
+                             err_handle, att_err);
     return rc;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 2e5da47..5a2e378 100644
--- a/net/nimble/host/src/ble_gatt.c
+++ b/net/nimble/host/src/ble_gatt.c
@@ -36,8 +36,7 @@ struct ble_gatt_entry {
     uint16_t conn_handle;
     union {
         struct {
-            int (*cb)(uint8_t ble_hs_status, uint8_t att_status,
-                      uint16_t conn_handle, uint16_t mtu,
+            int (*cb)(int status, uint16_t conn_handle, uint16_t mtu,
                       void *arg);
             void *cb_arg;
         } mtu;
@@ -91,8 +90,7 @@ struct ble_gatt_entry {
 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,
-                             uint8_t ble_hs_status, uint8_t att_status);
+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);
@@ -102,21 +100,15 @@ 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,
-                             uint8_t ble_hs_status, uint8_t att_status);
+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,
-                                           uint8_t ble_hs_status,
-                                           uint8_t att_status);
+                                           int status);
 static void ble_gatt_err_disc_service_uuid(struct ble_gatt_entry *entry,
-                                           uint8_t ble_hs_status,
-                                           uint8_t att_status);
+                                           int status);
 static void ble_gatt_err_disc_all_chars(struct ble_gatt_entry *entry,
-                                        uint8_t ble_hs_status,
-                                        uint8_t att_status);
-static void ble_gatt_err_read(struct ble_gatt_entry *entry,
-                              uint8_t ble_hs_status, uint8_t att_status);
-static void ble_gatt_err_write(struct ble_gatt_entry *entry,
-                               uint8_t ble_hs_status, uint8_t att_status);
+                                        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;
@@ -376,16 +368,14 @@ ble_gatt_tx_postpone_chk(struct ble_gatt_entry *entry, int rc)
  *****************************************************************************/
 
 static int
-ble_gatt_mtu_cb(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                uint8_t att_status, uint16_t mtu)
+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, ble_hs_status, att_status, mtu,
-                           entry->mtu.cb_arg);
+        rc = entry->mtu.cb(entry->conn_handle, status, mtu, entry->mtu.cb_arg);
     }
 
     return rc;
@@ -421,15 +411,14 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_mtu_cb(entry, rc, 0, 0);
+    ble_gatt_mtu_cb(entry, rc, 0);
     return rc;
 }
 
 static void
-ble_gatt_err_mtu(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                 uint8_t att_status)
+ble_gatt_err_mtu(struct ble_gatt_entry *entry, int status)
 {
-    ble_gatt_mtu_cb(entry, ble_hs_status, att_status, 0);
+    ble_gatt_mtu_cb(entry, status, 0);
 }
 
 void
@@ -444,7 +433,7 @@ ble_gatt_rx_mtu(struct ble_hs_conn *conn, uint16_t chan_mtu)
         return;
     }
 
-    ble_gatt_mtu_cb(entry, 0, 0, chan_mtu);
+    ble_gatt_mtu_cb(entry, 0, chan_mtu);
     ble_gatt_entry_remove_free(entry, prev);
 }
 
@@ -468,16 +457,14 @@ ble_gatt_exchange_mtu(uint16_t conn_handle)
 
 static int
 ble_gatt_disc_all_services_cb(struct ble_gatt_entry *entry,
-                              uint8_t ble_hs_status, uint8_t att_status,
-                              struct ble_gatt_service *service)
+                              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, ble_hs_status,
-                                         att_status, service,
+        rc = entry->disc_all_services.cb(entry->conn_handle, status, service,
                                          entry->disc_all_services.cb_arg);
     }
 
@@ -515,23 +502,19 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_disc_all_services_cb(entry, rc, 0, NULL);
+    ble_gatt_disc_all_services_cb(entry, rc, NULL);
     return rc;
 }
 
 static void
-ble_gatt_err_disc_all_services(struct ble_gatt_entry *entry,
-                               uint8_t ble_hs_status, uint8_t att_status)
+ble_gatt_err_disc_all_services(struct ble_gatt_entry *entry, int status)
 {
-    if (ble_hs_status == BLE_HS_EATT &&
-        att_status == BLE_ATT_ERR_ATTR_NOT_FOUND) {
-
+    if (status == BLE_HS_EATT_ATTR_NOT_FOUND) {
         /* Discovery is complete. */
-        ble_hs_status = 0;
-        att_status = 0;
+        status = 0;
     }
 
-    ble_gatt_disc_all_services_cb(entry, ble_hs_status, att_status, NULL);
+    ble_gatt_disc_all_services_cb(entry, status, NULL);
 }
 
 void
@@ -578,7 +561,7 @@ ble_gatt_rx_read_group_type_adata(struct ble_hs_conn *conn,
     rc = 0;
 
 done:
-    cbrc = ble_gatt_disc_all_services_cb(entry, rc, 0, &service);
+    cbrc = ble_gatt_disc_all_services_cb(entry, rc, &service);
     if (rc != 0 || cbrc != 0) {
         ble_gatt_entry_remove_free(entry, prev);
     }
@@ -599,7 +582,7 @@ ble_gatt_rx_read_group_type_complete(struct ble_hs_conn *conn, int rc)
 
     if (rc != 0 || entry->disc_all_services.prev_handle == 0xffff) {
         /* Error or all services discovered. */
-        ble_gatt_disc_all_services_cb(entry, rc, 0, NULL);
+        ble_gatt_disc_all_services_cb(entry, rc, NULL);
         ble_gatt_entry_remove_free(entry, prev);
     } else {
         /* Send follow-up request. */
@@ -631,8 +614,7 @@ ble_gatt_disc_all_services(uint16_t conn_handle, ble_gatt_disc_service_fn *cb,
  *****************************************************************************/
 
 static int
-ble_gatt_disc_service_uuid_cb(struct ble_gatt_entry *entry,
-                              uint8_t ble_hs_status, uint8_t att_status,
+ble_gatt_disc_service_uuid_cb(struct ble_gatt_entry *entry, int status,
                               struct ble_gatt_service *service)
 {
     int rc;
@@ -640,8 +622,7 @@ ble_gatt_disc_service_uuid_cb(struct ble_gatt_entry *entry,
     if (entry->disc_service_uuid.cb == NULL) {
         rc = 0;
     } else {
-        rc = entry->disc_service_uuid.cb(entry->conn_handle, ble_hs_status,
-                                         att_status, service,
+        rc = entry->disc_service_uuid.cb(entry->conn_handle, status, service,
                                          entry->disc_service_uuid.cb_arg);
     }
 
@@ -679,23 +660,19 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_disc_service_uuid_cb(entry, rc, 0, NULL);
+    ble_gatt_disc_service_uuid_cb(entry, rc, NULL);
     return rc;
 }
 
 static void
-ble_gatt_err_disc_service_uuid(struct ble_gatt_entry *entry,
-                               uint8_t ble_hs_status, uint8_t att_status)
+ble_gatt_err_disc_service_uuid(struct ble_gatt_entry *entry, int status)
 {
-    if (ble_hs_status == BLE_HS_EATT &&
-        att_status == BLE_ATT_ERR_ATTR_NOT_FOUND) {
-
+    if (status == BLE_HS_EATT_ATTR_NOT_FOUND) {
         /* Discovery is complete. */
-        ble_hs_status = 0;
-        att_status = 0;
+        status = 0;
     }
 
-    ble_gatt_disc_service_uuid_cb(entry, ble_hs_status, att_status, NULL);
+    ble_gatt_disc_service_uuid_cb(entry, status, NULL);
 }
 
 void
@@ -714,11 +691,13 @@ ble_gatt_rx_find_type_value_hinfo(struct ble_hs_conn *conn,
         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, 0, &service);
+    rc = ble_gatt_disc_service_uuid_cb(entry, 0, &service);
     if (rc != 0) {
         ble_gatt_entry_remove_free(entry, prev);
     }
@@ -739,7 +718,7 @@ ble_gatt_rx_find_type_value_complete(struct ble_hs_conn *conn, int rc)
 
     if (rc != 0 || entry->disc_service_uuid.prev_handle == 0xffff) {
         /* Error or all services discovered. */
-        ble_gatt_disc_service_uuid_cb(entry, rc, 0, NULL);
+        ble_gatt_disc_service_uuid_cb(entry, rc, NULL);
         ble_gatt_entry_remove_free(entry, prev);
     } else {
         /* Send follow-up request. */
@@ -772,16 +751,15 @@ ble_gatt_disc_service_by_uuid(uint16_t conn_handle, void *service_uuid128,
  *****************************************************************************/
 
 static int
-ble_gatt_disc_all_chars_cb(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                           uint8_t att_status, struct ble_gatt_chr *chr)
+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, ble_hs_status,
-                                      att_status, chr,
+        rc = entry->disc_all_chars.cb(entry->conn_handle, status, chr,
                                       entry->disc_all_chars.cb_arg);
     }
 
@@ -820,23 +798,19 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_disc_all_chars_cb(entry, rc, 0, NULL);
+    ble_gatt_disc_all_chars_cb(entry, rc, NULL);
     return rc;
 }
 
 static void
-ble_gatt_err_disc_all_chars(struct ble_gatt_entry *entry,
-                            uint8_t ble_hs_status, uint8_t att_status)
+ble_gatt_err_disc_all_chars(struct ble_gatt_entry *entry, int status)
 {
-    if (ble_hs_status == BLE_HS_EATT &&
-        att_status == BLE_ATT_ERR_ATTR_NOT_FOUND) {
-
+    if (status == BLE_HS_EATT_ATTR_NOT_FOUND) {
         /* Discovery is complete. */
-        ble_hs_status = 0;
-        att_status = 0;
+        status = 0;
     }
 
-    ble_gatt_disc_all_chars_cb(entry, ble_hs_status, att_status, NULL);
+    ble_gatt_disc_all_chars_cb(entry, status, NULL);
 }
 
 void
@@ -879,15 +853,15 @@ ble_gatt_rx_read_type_adata(struct ble_hs_conn *conn,
         goto done;
     }
 
+    entry->disc_all_chars.prev_handle = adata->att_handle;
+
     chr.properties = adata->value[0];
     chr.value_handle = le16toh(adata->value + 1);
 
-    entry->disc_all_chars.prev_handle = adata->att_handle;
-
     rc = 0;
 
 done:
-    cbrc = ble_gatt_disc_all_chars_cb(entry, rc, 0, &chr);
+    cbrc = ble_gatt_disc_all_chars_cb(entry, rc, &chr);
     if (rc != 0 || cbrc != 0) {
         ble_gatt_entry_remove_free(entry, prev);
     }
@@ -909,7 +883,7 @@ ble_gatt_rx_read_type_complete(struct ble_hs_conn *conn, int rc)
     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, 0, NULL);
+        ble_gatt_disc_all_chars_cb(entry, rc, NULL);
         ble_gatt_entry_remove_free(entry, prev);
     } else {
         /* Send follow-up request. */
@@ -942,16 +916,16 @@ ble_gatt_disc_all_chars(uint16_t conn_handle, uint16_t start_handle,
  *****************************************************************************/
 
 static int
-ble_gatt_read_cb(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                 uint8_t att_status, struct ble_gatt_attr *attr)
+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, ble_hs_status, att_status,
-                            attr, entry->read.cb_arg);
+        rc = entry->read.cb(entry->conn_handle, status, attr,
+                            entry->read.cb_arg);
     }
 
     return rc;
@@ -983,15 +957,14 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_read_cb(entry, rc, 0, NULL);
+    ble_gatt_read_cb(entry, rc, NULL);
     return rc;
 }
 
 static void
-ble_gatt_err_read(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                  uint8_t att_status)
+ble_gatt_err_read(struct ble_gatt_entry *entry, int status)
 {
-    ble_gatt_read_cb(entry, ble_hs_status, att_status, NULL);
+    ble_gatt_read_cb(entry, status, NULL);
 }
 
 void
@@ -1012,7 +985,7 @@ ble_gatt_rx_read_rsp(struct ble_hs_conn *conn, int status, void *value,
     attr.value_len = value_len;
     attr.value = value;
 
-    ble_gatt_read_cb(entry, status, 0, &attr);
+    ble_gatt_read_cb(entry, status, &attr);
 
     /* The read operation only has a single request / response exchange. */
     ble_gatt_entry_remove_free(entry, prev);
@@ -1041,16 +1014,15 @@ ble_gatt_read(uint16_t conn_handle, uint16_t attr_handle,
  *****************************************************************************/
 
 static int
-ble_gatt_write_cb(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                  uint8_t att_status)
+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, ble_hs_status, att_status,
-                             &entry->write.attr, entry->write.cb_arg);
+        rc = entry->write.cb(entry->conn_handle, status, &entry->write.attr,
+                             entry->write.cb_arg);
     }
 
     return rc;
@@ -1079,7 +1051,7 @@ ble_gatt_kick_write_no_rsp(struct ble_gatt_entry *entry)
     /* No response expected; call callback immediately and return nonzero to
      * indicate the entry should be freed.
      */
-    ble_gatt_write_cb(entry, 0, 0);
+    ble_gatt_write_cb(entry, 0);
 
     return 1;
 
@@ -1088,7 +1060,7 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_write_cb(entry, rc, 0);
+    ble_gatt_write_cb(entry, rc);
     return rc;
 }
 
@@ -1144,15 +1116,14 @@ err:
         return BLE_HS_EAGAIN;
     }
 
-    ble_gatt_write_cb(entry, rc, 0);
+    ble_gatt_write_cb(entry, rc);
     return rc;
 }
 
 static void
-ble_gatt_err_write(struct ble_gatt_entry *entry, uint8_t ble_hs_status,
-                   uint8_t att_status)
+ble_gatt_err_write(struct ble_gatt_entry *entry, int status)
 {
-    ble_gatt_write_cb(entry, ble_hs_status, att_status);
+    ble_gatt_write_cb(entry, status);
 }
 
 void
@@ -1167,7 +1138,7 @@ ble_gatt_rx_write_rsp(struct ble_hs_conn *conn)
         return;
     }
 
-    ble_gatt_write_cb(entry, 0, 0);
+    ble_gatt_write_cb(entry, 0);
 
     /* The write operation only has a single request / response exchange. */
     ble_gatt_entry_remove_free(entry, prev);
@@ -1258,7 +1229,7 @@ ble_gatt_rx_err(uint16_t conn_handle, struct ble_att_error_rsp *rsp)
 
     dispatch = ble_gatt_dispatch_get(entry->op);
     if (dispatch->err_cb != NULL) {
-        dispatch->err_cb(entry, BLE_HS_EATT, rsp->baep_error_code);
+        dispatch->err_cb(entry, BLE_HS_ERR_ATT_BASE + rsp->baep_error_code);
     }
 
     ble_gatt_entry_remove_free(entry, prev);
@@ -1283,7 +1254,7 @@ ble_gatt_connection_broken(uint16_t conn_handle)
         }
 
         dispatch = ble_gatt_dispatch_get(entry->op);
-        dispatch->err_cb(entry, BLE_HS_ENOTCONN, 0);
+        dispatch->err_cb(entry, BLE_HS_ENOTCONN);
 
         ble_gatt_entry_remove_free(entry, prev);
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/ble_hs_cfg.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_cfg.c b/net/nimble/host/src/ble_hs_cfg.c
index b612b84..e29b3ef 100644
--- a/net/nimble/host/src/ble_hs_cfg.c
+++ b/net/nimble/host/src/ble_hs_cfg.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_hs_priv.h"
 
 static struct ble_hs_cfg ble_hs_cfg_dflt = {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/ble_hs_misc.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_misc.c b/net/nimble/host/src/ble_hs_misc.c
index 9c076f5..42f4596 100644
--- a/net/nimble/host/src/ble_hs_misc.c
+++ b/net/nimble/host/src/ble_hs_misc.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 <stdlib.h>
 #include "os/os.h"
 #include "ble_hs_priv.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 7dad447..c52b538 100644
--- a/net/nimble/host/src/test/ble_gatt_conn_test.c
+++ b/net/nimble/host/src/test/ble_gatt_conn_test.c
@@ -34,10 +34,9 @@
 static uint8_t ble_gatt_conn_test_write_value[] = { 1, 3, 64, 21, 6 };
 
 static int
-ble_gatt_conn_test_disc_service_cb(uint16_t conn_handle,
-                                    uint8_t ble_hs_status, uint8_t att_status,
-                                    struct ble_gatt_service *service,
-                                    void *arg)
+ble_gatt_conn_test_disc_service_cb(uint16_t conn_handle, int status,
+                                   struct ble_gatt_service *service,
+                                   void *arg)
 {
     int *called;
 
@@ -45,17 +44,15 @@ ble_gatt_conn_test_disc_service_cb(uint16_t conn_handle,
     *called = 1;
 
     TEST_ASSERT(conn_handle == BLE_GATT_BREAK_TEST_DISC_SERVICE_HANDLE);
-    TEST_ASSERT(ble_hs_status == BLE_HS_ENOTCONN);
-    TEST_ASSERT(att_status == 0);
+    TEST_ASSERT(status == BLE_HS_ENOTCONN);
     TEST_ASSERT(service == NULL);
 
     return 0;
 }
 
 static int
-ble_gatt_conn_test_disc_chr_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                                uint8_t att_status, struct ble_gatt_chr *chr,
-                                void *arg)
+ble_gatt_conn_test_disc_chr_cb(uint16_t conn_handle, int status,
+                               struct ble_gatt_chr *chr, void *arg)
 {
     int *called;
 
@@ -63,17 +60,15 @@ ble_gatt_conn_test_disc_chr_cb(uint16_t conn_handle, uint8_t ble_hs_status,
     *called = 1;
 
     TEST_ASSERT(conn_handle == BLE_GATT_BREAK_TEST_DISC_CHR_HANDLE);
-    TEST_ASSERT(ble_hs_status == BLE_HS_ENOTCONN);
-    TEST_ASSERT(att_status == 0);
+    TEST_ASSERT(status == BLE_HS_ENOTCONN);
     TEST_ASSERT(chr == NULL);
 
     return 0;
 }
 
 static int
-ble_gatt_conn_test_write_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                             uint8_t att_status, struct ble_gatt_attr *attr,
-                             void *arg)
+ble_gatt_conn_test_write_cb(uint16_t conn_handle, int status,
+                            struct ble_gatt_attr *attr, void *arg)
 {
     int *called;
 
@@ -81,8 +76,7 @@ ble_gatt_conn_test_write_cb(uint16_t conn_handle, uint8_t ble_hs_status,
     *called = 1;
 
     TEST_ASSERT(conn_handle == BLE_GATT_BREAK_TEST_WRITE_HANDLE);
-    TEST_ASSERT(ble_hs_status == BLE_HS_ENOTCONN);
-    TEST_ASSERT(att_status == 0);
+    TEST_ASSERT(status == BLE_HS_ENOTCONN);
     TEST_ASSERT(attr != NULL);
     TEST_ASSERT(attr->handle == BLE_GATT_BREAK_TEST_WRITE_ATTR_HANDLE);
     TEST_ASSERT(attr->value_len == sizeof ble_gatt_conn_test_write_value);
@@ -93,9 +87,8 @@ ble_gatt_conn_test_write_cb(uint16_t conn_handle, uint8_t ble_hs_status,
 }
 
 static int
-ble_gatt_conn_test_read_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                            uint8_t att_status, struct ble_gatt_attr *attr,
-                            void *arg)
+ble_gatt_conn_test_read_cb(uint16_t conn_handle, int status,
+                           struct ble_gatt_attr *attr, void *arg)
 {
     int *called;
 
@@ -103,8 +96,7 @@ ble_gatt_conn_test_read_cb(uint16_t conn_handle, uint8_t ble_hs_status,
     *called = 1;
 
     TEST_ASSERT(conn_handle == BLE_GATT_BREAK_TEST_READ_HANDLE);
-    TEST_ASSERT(ble_hs_status == BLE_HS_ENOTCONN);
-    TEST_ASSERT(att_status == 0);
+    TEST_ASSERT(status == BLE_HS_ENOTCONN);
     TEST_ASSERT(attr == NULL);
 
     return 0;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/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 b0e22ef..19857c9 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
@@ -136,7 +136,8 @@ ble_gatt_disc_c_test_misc_rx_all_rsp(struct ble_hs_conn *conn,
         /* Send the pending ATT Request. */
         ble_gatt_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_TYPE_REQ,
-                                        BLE_ATT_ERR_ATTR_NOT_FOUND);
+                                        BLE_ATT_ERR_ATTR_NOT_FOUND,
+                                        chars[idx - 1].decl_handle);
     }
 }
 
@@ -166,13 +167,12 @@ ble_gatt_disc_c_test_misc_verify_chars(struct ble_gatt_disc_c_test_char *chars)
 }
 
 static int
-ble_gatt_disc_c_test_misc_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                             uint8_t att_status, struct ble_gatt_chr *chr,
-                             void *arg)
+ble_gatt_disc_c_test_misc_cb(uint16_t conn_handle, int status,
+                             struct ble_gatt_chr *chr, void *arg)
 {
     struct ble_gatt_chr *dst;
 
-    TEST_ASSERT(ble_hs_status == 0 && att_status == 0);
+    TEST_ASSERT(status == 0);
     TEST_ASSERT(!ble_gatt_disc_c_test_rx_complete);
 
     if (chr == NULL) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/test/ble_gatt_disc_s_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_disc_s_test.c b/net/nimble/host/src/test/ble_gatt_disc_s_test.c
index 2dc33ab..c2187ca 100644
--- a/net/nimble/host/src/test/ble_gatt_disc_s_test.c
+++ b/net/nimble/host/src/test/ble_gatt_disc_s_test.c
@@ -126,11 +126,12 @@ ble_gatt_disc_s_test_misc_rx_all_rsp(
         idx += count;
     }
 
-    if (services[idx - 1].start_handle != 0xffff) {
+    if (services[idx - 1].end_handle != 0xffff) {
         /* Send the pending ATT Request. */
         ble_gatt_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_GROUP_TYPE_REQ,
-                                        BLE_ATT_ERR_ATTR_NOT_FOUND);
+                                        BLE_ATT_ERR_ATTR_NOT_FOUND,
+                                        services[idx - 1].start_handle);
     }
 }
 
@@ -185,11 +186,12 @@ ble_gatt_disc_s_test_misc_rx_uuid_rsp(
         idx += count;
     }
 
-    if (services[idx - 1].start_handle != 0xffff) {
+    if (services[idx - 1].end_handle != 0xffff) {
         /* Send the pending ATT Request. */
         ble_gatt_wakeup();
         ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_FIND_TYPE_VALUE_REQ,
-                                        BLE_ATT_ERR_ATTR_NOT_FOUND);
+                                        BLE_ATT_ERR_ATTR_NOT_FOUND,
+                                        services[idx - 1].start_handle);
     }
 }
 
@@ -221,11 +223,10 @@ ble_gatt_disc_s_test_misc_verify_services(
 }
 
 static int
-ble_gatt_disc_s_test_misc_disc_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                                  uint8_t att_status,
+ble_gatt_disc_s_test_misc_disc_cb(uint16_t conn_handle, int status,
                                   struct ble_gatt_service *service, void *arg)
 {
-    TEST_ASSERT(ble_hs_status == 0 && att_status == 0);
+    TEST_ASSERT(status == 0);
     TEST_ASSERT(!ble_gatt_disc_s_test_rx_complete);
 
     if (service == NULL) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/test/ble_gatt_read_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_read_test.c b/net/nimble/host/src/test/ble_gatt_read_test.c
index 5b052a4..88cec80 100644
--- a/net/nimble/host/src/test/ble_gatt_read_test.c
+++ b/net/nimble/host/src/test/ble_gatt_read_test.c
@@ -20,24 +20,31 @@
 #include "nimble/ble.h"
 #include "host/ble_hs_test.h"
 #include "host/ble_hs_uuid.h"
+#include "ble_hs_priv.h"
 #include "ble_gatt_priv.h"
 #include "ble_att_cmd.h"
 #include "ble_hs_conn.h"
 #include "ble_hs_test_util.h"
 
+static uint16_t ble_gatt_read_test_conn_handle;
+static int ble_gatt_read_test_status;
+struct ble_gatt_attr ble_gatt_read_test_attr;
 static uint8_t ble_gatt_read_test_attr_val[1024];
-static int ble_gatt_read_test_attr_len;
 
 static int
-ble_gatt_read_test_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                      uint8_t att_status, struct ble_gatt_attr *attr,
-                      void *arg)
+ble_gatt_read_test_cb(uint16_t conn_handle, int status,
+                      struct ble_gatt_attr *attr, void *arg)
 {
-    TEST_ASSERT(ble_hs_status == 0 && att_status == 0);
     TEST_ASSERT_FATAL(attr->value_len <= sizeof ble_gatt_read_test_attr_val);
 
-    memcpy(ble_gatt_read_test_attr_val, attr->value, attr->value_len);
-    ble_gatt_read_test_attr_len = attr->value_len;
+    ble_gatt_read_test_conn_handle = conn_handle;
+    ble_gatt_read_test_status = status;
+
+    if (status == 0) {
+        TEST_ASSERT_FATAL(attr != NULL);
+        ble_gatt_read_test_attr = *attr;
+        memcpy(ble_gatt_read_test_attr_val, attr->value, attr->value_len);
+    }
 
     return 0;
 }
@@ -67,6 +74,18 @@ ble_gatt_read_test_misc_rx_rsp_good(struct ble_hs_conn *conn,
 }
 
 static void
+ble_gatt_read_test_misc_rx_rsp_bad(struct ble_hs_conn *conn,
+                                   struct ble_gatt_attr *attr,
+                                   uint8_t att_error)
+{
+    /* Send the pending ATT Read Request. */
+    ble_gatt_wakeup();
+
+    ble_hs_test_util_rx_att_err_rsp(conn, BLE_ATT_OP_READ_REQ, att_error,
+                                    attr->handle);
+}
+
+static void
 ble_gatt_read_test_misc_verify_good(struct ble_hs_conn *conn,
                                     struct ble_gatt_attr *attr)
 {
@@ -78,11 +97,31 @@ ble_gatt_read_test_misc_verify_good(struct ble_hs_conn *conn,
 
     ble_gatt_read_test_misc_rx_rsp_good(conn, attr);
 
-    TEST_ASSERT(ble_gatt_read_test_attr_len == attr->value_len);
+    TEST_ASSERT(ble_gatt_read_test_conn_handle == conn->bhc_handle);
+    TEST_ASSERT(ble_gatt_read_test_status == 0);
+    TEST_ASSERT(ble_gatt_read_test_attr.handle == attr->handle);
+    TEST_ASSERT(ble_gatt_read_test_attr.value_len == attr->value_len);
     TEST_ASSERT(memcmp(ble_gatt_read_test_attr_val, attr->value,
                        attr->value_len) == 0);
 }
 
+static void
+ble_gatt_read_test_misc_verify_bad(struct ble_hs_conn *conn,
+                                   uint8_t att_status,
+                                   struct ble_gatt_attr *attr)
+{
+    int rc;
+
+    rc = ble_gatt_read(conn->bhc_handle, attr->handle, ble_gatt_read_test_cb,
+                       NULL);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_gatt_read_test_misc_rx_rsp_bad(conn, attr, att_status);
+
+    TEST_ASSERT(ble_gatt_read_test_conn_handle == conn->bhc_handle);
+    TEST_ASSERT(ble_gatt_read_test_status == BLE_HS_ERR_ATT_BASE + att_status);
+}
+
 TEST_CASE(ble_gatt_read_test_by_handle)
 {
     struct ble_hs_conn *conn;
@@ -111,6 +150,22 @@ TEST_CASE(ble_gatt_read_test_by_handle)
         .value = (uint8_t[200]){ 0 },
         .value_len = 200,
     } });
+
+    /* Fail due to attribute not found. */
+    ble_gatt_read_test_misc_verify_bad(conn, BLE_ATT_ERR_ATTR_NOT_FOUND,
+        (struct ble_gatt_attr[]) { {
+            .handle = 719,
+            .value = (uint8_t[]){ 1,2,3,4,5,6,7 },
+            .value_len = 7
+        } });
+
+    /* Fail due to invalid PDU. */
+    ble_gatt_read_test_misc_verify_bad(conn, BLE_ATT_ERR_INVALID_PDU,
+        (struct ble_gatt_attr[]) { {
+            .handle = 65,
+            .value = (uint8_t[]){ 0xfa, 0x4c },
+            .value_len = 2
+        } });
 }
 
 TEST_SUITE(gle_gatt_read_test_suite)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/test/ble_gatt_write_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gatt_write_test.c b/net/nimble/host/src/test/ble_gatt_write_test.c
index a81dbea..141348f 100644
--- a/net/nimble/host/src/test/ble_gatt_write_test.c
+++ b/net/nimble/host/src/test/ble_gatt_write_test.c
@@ -39,13 +39,11 @@ ble_gatt_write_test_init(void)
 }
 
 static int
-ble_gatt_write_test_cb(uint16_t conn_handle, uint8_t ble_hs_status,
-                       uint8_t att_status, struct ble_gatt_attr *attr,
-                       void *arg)
+ble_gatt_write_test_cb(uint16_t conn_handle, int status,
+                       struct ble_gatt_attr *attr, void *arg)
 {
     TEST_ASSERT(conn_handle == 2);
-    TEST_ASSERT(ble_hs_status == 0);
-    TEST_ASSERT(att_status == 0);
+    TEST_ASSERT(status == 0);
     TEST_ASSERT(attr->handle == 100);
     TEST_ASSERT(attr->value_len == sizeof ble_gatt_write_test_attr_value);
     TEST_ASSERT(attr->value == ble_gatt_write_test_attr_value);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/test/ble_hs_test_util.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test_util.c b/net/nimble/host/src/test/ble_hs_test_util.c
index a605206..4f4f49d 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.c
+++ b/net/nimble/host/src/test/ble_hs_test_util.c
@@ -180,7 +180,7 @@ ble_hs_test_util_l2cap_rx_payload_flat(struct ble_hs_conn *conn,
 
 void
 ble_hs_test_util_rx_att_err_rsp(struct ble_hs_conn *conn, uint8_t req_op,
-                                uint8_t error_code)
+                                uint8_t error_code, uint16_t err_handle)
 {
     struct ble_att_error_rsp rsp;
     struct ble_l2cap_chan *chan;
@@ -188,7 +188,7 @@ ble_hs_test_util_rx_att_err_rsp(struct ble_hs_conn *conn, uint8_t req_op,
     int rc;
 
     rsp.baep_req_op = req_op;
-    rsp.baep_handle = conn->bhc_handle;
+    rsp.baep_handle = err_handle;
     rsp.baep_error_code = error_code;
 
     rc = ble_att_error_rsp_write(buf, sizeof buf, &rsp);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/net/nimble/host/src/test/ble_hs_test_util.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test_util.h b/net/nimble/host/src/test/ble_hs_test_util.h
index 0df60dc..1a8cf4a 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.h
+++ b/net/nimble/host/src/test/ble_hs_test_util.h
@@ -48,7 +48,7 @@ int ble_hs_test_util_l2cap_rx_payload_flat(struct ble_hs_conn *conn,
                                            const void *data, int len);
 void ble_hs_test_util_rx_hci_buf_size_ack(uint16_t buf_size);
 void ble_hs_test_util_rx_att_err_rsp(struct ble_hs_conn *conn, uint8_t req_op,
-                                     uint8_t error_code);
+                                     uint8_t error_code, uint16_t err_handle);
 void ble_hs_test_util_rx_startup_acks(void);
 void ble_hs_test_util_rx_num_completed_pkts_event(
     struct ble_hs_test_util_num_completed_pkts_entry *entries);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/14f9937e/project/hostctlrtest/src/main.c
----------------------------------------------------------------------
diff --git a/project/hostctlrtest/src/main.c b/project/hostctlrtest/src/main.c
index 92320d0..ff9ddf2 100755
--- a/project/hostctlrtest/src/main.c
+++ b/project/hostctlrtest/src/main.c
@@ -17,7 +17,7 @@
 #define HOSTCTLRTEST_ROLE_SCANNER            (1)
 #define HOSTCTLRTEST_ROLE_ADVERTISER         (2)
 
-#define HOSTCTLRTEST_CFG_ROLE                (0)
+#define HOSTCTLRTEST_CFG_ROLE                (2)
 
 #include <assert.h>
 #include <string.h>
@@ -90,16 +90,14 @@ bletest_inc_adv_pkt_num(void) { }
 #if HOSTCTLRTEST_CFG_ROLE == HOSTCTLRTEST_ROLE_INITIATOR
 
 static int
-hostctlrtest_on_read(uint16_t conn_handle, uint8_t ble_hs_status,
-                     uint8_t att_status, struct ble_gatt_attr *attr,
-                     void *arg)
+hostctlrtest_on_read(uint16_t conn_handle, int status,
+                     struct ble_gatt_attr *attr, void *arg)
 {
     uint8_t *u8ptr;
     int i;
 
-    if (ble_hs_status != 0) {
-        console_printf("characteristic read failure: ble_hs_status=%d "
-                       "att_status=%d\n", ble_hs_status, att_status);
+    if (status != 0) {
+        console_printf("characteristic read failure: status=%d\n", status);
         return 0;
     }
 
@@ -117,15 +115,14 @@ hostctlrtest_on_read(uint16_t conn_handle, uint8_t ble_hs_status,
 }
 
 static int
-hostctlrtest_on_disc_c(uint16_t conn_handle, uint8_t ble_hs_status,
-                       uint8_t att_status, struct ble_gatt_chr *chr,
-                       void *arg)
+hostctlrtest_on_disc_c(uint16_t conn_handle, int status,
+                       struct ble_gatt_chr *chr, void *arg)
 {
     int i;
 
-    if (ble_hs_status != 0) {
-        console_printf("characteristic discovery failure: ble_hs_status=%d "
-                       "att_status=%d\n", ble_hs_status, att_status);
+    if (status != 0) {
+        console_printf("characteristic discovery failure: status=%d\n",
+                       status);
         return 0;
     }
 
@@ -151,15 +148,13 @@ hostctlrtest_on_disc_c(uint16_t conn_handle, uint8_t ble_hs_status,
 }
 
 static int
-hostctlrtest_on_disc_s(uint16_t conn_handle, uint8_t ble_hs_status,
-                       uint8_t att_status, struct ble_gatt_service *service,
-                       void *arg)
+hostctlrtest_on_disc_s(uint16_t conn_handle, int status,
+                       struct ble_gatt_service *service, void *arg)
 {
     int i;
 
-    if (ble_hs_status != 0) {
-        console_printf("service discovery failure: ble_hs_status=%d "
-                       "att_status=%d\n", ble_hs_status, att_status);
+    if (status != 0) {
+        console_printf("service discovery failure: status=%d\n", status);
         return 0;
     }