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/11/06 01:55:48 UTC

incubator-mynewt-larva git commit: Send error response on rx of bad attr read req.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master 0ab9c8dc7 -> 34d3b2e51


Send error response on rx of bad attr read req.


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

Branch: refs/heads/master
Commit: 34d3b2e51c5d80a92f60de6d70911609098bfc45
Parents: 0ab9c8d
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Nov 5 16:55:07 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Thu Nov 5 16:55:07 2015 -0800

----------------------------------------------------------------------
 libs/os/src/os_mbuf.c                      |  2 +-
 net/nimble/host/include/host/ble_hs_test.h |  1 +
 net/nimble/host/src/ble_hs.c               |  7 +-
 net/nimble/host/src/ble_hs_att.c           | 76 +++++++++++++-------
 net/nimble/host/src/ble_hs_att.h           |  6 +-
 net/nimble/host/src/ble_hs_att_cmd.c       | 90 ++++++++++++++++++++++++
 net/nimble/host/src/ble_hs_att_cmd.h       | 58 ++++++++++++++++
 net/nimble/host/src/ble_hs_itf.h           |  3 -
 net/nimble/host/src/ble_l2cap.c            | 76 +++++++++++++++++---
 net/nimble/host/src/ble_l2cap.h            | 16 +++--
 net/nimble/host/src/test/ble_hs_att_test.c | 92 +++++++++++++++++++++++++
 net/nimble/host/src/test/ble_hs_test.c     | 77 +++++----------------
 net/nimble/host/src/test/ble_l2cap_test.c  | 78 +++++++++++++++++++++
 13 files changed, 469 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/libs/os/src/os_mbuf.c
----------------------------------------------------------------------
diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c
index 6b4a0af..6e3b745 100644
--- a/libs/os/src/os_mbuf.c
+++ b/libs/os/src/os_mbuf.c
@@ -347,7 +347,7 @@ os_mbuf_copydata(const struct os_mbuf *m, int off, int len, void *dst)
         off -= m->om_len;
         m = SLIST_NEXT(m, om_next);
     }
-    while (len > 0) {
+    while (len > 0 && m != NULL) {
         count = min(m->om_len - off, len);
         memcpy(udst, m->om_data + off, count);
         len -= count;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/include/host/ble_hs_test.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_hs_test.h b/net/nimble/host/include/host/ble_hs_test.h
index 92d020e..e1d67fc 100644
--- a/net/nimble/host/include/host/ble_hs_test.h
+++ b/net/nimble/host/include/host/ble_hs_test.h
@@ -18,6 +18,7 @@
 #define H_HOST_TEST_
 
 int l2cap_test_all(void);
+int ble_hs_att_test_all(void);
 
 #endif
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index e81c42e..ba13da6 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -27,7 +27,6 @@
 int ble_host_listen_enabled;
 
 static struct os_eventq host_task_evq;
-//static struct ble_att_chan default_attr_chan; 
 static struct os_callout ble_host_task_timer;
 
 int
@@ -79,7 +78,6 @@ ble_host_task_handler(void *arg)
         switch (ev->ev_type) {
             case OS_EVENT_T_TIMER:
                 /* Poll the attribute channel */
-                //ble_att_chan_poll(&default_attr_chan, &host_task_evq);            
                 /* Reset callout, wakeup every 50ms */
                 os_callout_reset(&ble_host_task_timer, 50);
                 break;
@@ -110,6 +108,11 @@ host_init(void)
         return rc;
     }
 
+    rc = ble_hs_att_init();
+    if (rc != 0) {
+        return rc;
+    }
+
 #ifdef ARCH_sim
     if (ble_host_listen_enabled) {
         int rc;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs_att.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att.c b/net/nimble/host/src/ble_hs_att.c
index c2e0fc8..e8a80ae 100644
--- a/net/nimble/host/src/ble_hs_att.c
+++ b/net/nimble/host/src/ble_hs_att.c
@@ -22,6 +22,7 @@
 #include "ble_l2cap.h"
 #include "ble_l2cap_util.h"
 #include "ble_hs_conn.h"
+#include "ble_hs_att_cmd.h"
 #include "ble_hs_att.h"
 
 typedef int ble_hs_att_rx_fn(struct ble_hs_conn *conn,
@@ -31,14 +32,12 @@ struct ble_hs_att_rx_dispatch_entry {
     ble_hs_att_rx_fn *bde_fn;
 };
 
-#define BLE_HS_ATT_OP_READ          0x0a
-
 /** Dispatch table for incoming ATT requests.  Sorted by op code. */
 static int ble_hs_att_rx_read(struct ble_hs_conn *conn,
                               struct ble_l2cap_chan *chan);
 
 struct ble_hs_att_rx_dispatch_entry ble_hs_att_rx_dispatch[] = {
-    { BLE_HS_ATT_OP_READ, ble_hs_att_rx_read },
+    { BLE_HS_ATT_OP_READ_REQ, ble_hs_att_rx_read },
 };
 
 #define BLE_HS_ATT_RX_DISPATCH_SZ \
@@ -61,7 +60,7 @@ host_attr_list_lock(void)
     int rc;
 
     rc = os_mutex_pend(&g_host_attr_list_mutex, OS_WAIT_FOREVER);
-    if (rc != 0) {
+    if (rc != 0 && rc != OS_NOT_STARTED) {
         goto err;
     }
 
@@ -81,7 +80,7 @@ host_attr_list_unlock(void)
     int rc;
 
     rc = os_mutex_release(&g_host_attr_list_mutex);
-    if (rc != 0) {
+    if (rc != 0 && rc != OS_NOT_STARTED) {
         goto err;
     }
 
@@ -284,40 +283,56 @@ ble_hs_att_rx_dispatch_entry_find(uint8_t op)
     return NULL;
 }
 
-/**
- * | Parameter          | Size (octets) |
- * +--------------------+---------------+
- * | Attribute Opcode   | 1             |
- * | Attribute Handle   | 2             |
- */
 static int
-ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+ble_hs_att_tx_error_rsp(struct ble_l2cap_chan *chan, uint8_t req_op,
+                        uint16_t handle, uint8_t error_code)
 {
-    uint16_t handle;
-    uint8_t op;
-    int off;
+    struct ble_hs_att_error_rsp rsp;
+    uint8_t buf[BLE_HS_ATT_ERROR_RSP_SZ];
     int rc;
 
-    off = 0;
+    rsp.bhaep_op = BLE_HS_ATT_OP_ERROR_RSP;
+    rsp.bhaep_req_op = req_op;
+    rsp.bhaep_handle = handle;
+    rsp.bhaep_error_code = error_code;
+
+    rc = ble_hs_att_error_rsp_write(buf, sizeof buf, &rsp);
+    assert(rc == 0);
 
-    rc = os_mbuf_copydata(chan->blc_rx_buf, off, 1, &op);
+    rc = ble_l2cap_tx(chan, buf, sizeof buf);
     if (rc != 0) {
-        return EMSGSIZE;
+        return rc;
     }
-    off += 1;
 
-    assert(op == BLE_HS_ATT_OP_READ);
+    return 0;
+}
 
-    rc = ble_l2cap_read_uint16(chan, off, &handle);
+static int
+ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+{
+    struct ble_hs_att_read_req req;
+    struct host_attr *attr;
+    uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
+    int rc;
+
+    rc = os_mbuf_copydata(chan->blc_rx_buf, 0, sizeof buf, buf);
     if (rc != 0) {
-        return EMSGSIZE;
+        return rc;
     }
-    off += 2;
+    /* Strip ATT read request from the buffer. */
+    ble_l2cap_strip(chan, BLE_HS_ATT_READ_REQ_SZ);
 
-    /* XXX: Perform read. */
+    rc = ble_hs_att_read_req_parse(buf, sizeof buf, &req);
+    assert(rc == 0);
 
-    /* Strip ATT read request from the buffer. */
-    ble_l2cap_strip(chan, off);
+    rc = host_attr_find_by_handle(req.bharq_handle, &attr);
+    if (rc != 0) {
+        ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_READ_REQ,
+                                req.bharq_handle, rc);
+        return rc;
+    }
+
+    /* XXX: Send response. */
 
     return 0;
 }
@@ -362,3 +377,12 @@ ble_hs_att_create_chan(void)
 
     return chan;
 }
+
+int
+ble_hs_att_init(void)
+{
+    int rc;
+
+    rc = os_mutex_init(&g_host_attr_list_mutex);
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs_att.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att.h b/net/nimble/host/src/ble_hs_att.h
index feb8c2b..47d821f 100644
--- a/net/nimble/host/src/ble_hs_att.h
+++ b/net/nimble/host/src/ble_hs_att.h
@@ -21,11 +21,6 @@
 
 #define BLE_HOST_EVENT_NEW_ATTR_CONN (OS_EVENT_T_PERUSER)
 
-struct ble_att_chan {
-    int c_fd;
-    int c_state; 
-}; 
-
 struct host_attr;
 
 /**
@@ -73,5 +68,6 @@ struct host_attr {
 #define HA_METH_EXCHANGE_MTU_RSP (0x03)
 
 struct ble_l2cap_chan *ble_hs_att_create_chan(void);
+int ble_hs_att_init(void);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs_att_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att_cmd.c b/net/nimble/host/src/ble_hs_att_cmd.c
new file mode 100644
index 0000000..ea4f59d
--- /dev/null
+++ b/net/nimble/host/src/ble_hs_att_cmd.c
@@ -0,0 +1,90 @@
+#include <errno.h>
+#include "os/os.h"
+#include "nimble/ble.h"
+#include "ble_l2cap.h"
+#include "ble_l2cap_util.h"
+#include "ble_hs_att_cmd.h"
+
+int
+ble_hs_att_read_req_parse(void *payload, int len,
+                          struct ble_hs_att_read_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_READ_REQ_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    req->bharq_op = u8ptr[0];
+    req->bharq_handle = le16toh(u8ptr + 1);
+
+    if (req->bharq_op != BLE_HS_ATT_OP_READ_REQ) {
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+int
+ble_hs_att_read_req_write(void *payload, int len,
+                          struct ble_hs_att_read_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_READ_REQ_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    u8ptr[0] = req->bharq_op;
+    htole16(u8ptr + 1, req->bharq_handle);
+
+    return 0;
+}
+
+int
+ble_hs_att_error_rsp_parse(void *payload, int len,
+                           struct ble_hs_att_error_rsp *rsp)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_ERROR_RSP_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    rsp->bhaep_op = u8ptr[0];
+    rsp->bhaep_req_op = u8ptr[1];
+    rsp->bhaep_handle = le16toh(u8ptr + 2);
+    rsp->bhaep_error_code = u8ptr[4];
+
+    if (rsp->bhaep_op != BLE_HS_ATT_OP_ERROR_RSP) {
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+int
+ble_hs_att_error_rsp_write(void *payload, int len,
+                           struct ble_hs_att_error_rsp *rsp)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_ERROR_RSP_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    u8ptr[0] = rsp->bhaep_op;
+    u8ptr[1] = rsp->bhaep_req_op;
+    htole16(u8ptr + 2, rsp->bhaep_handle);
+    u8ptr[4] = rsp->bhaep_error_code;
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs_att_cmd.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_att_cmd.h b/net/nimble/host/src/ble_hs_att_cmd.h
new file mode 100644
index 0000000..5c139f1
--- /dev/null
+++ b/net/nimble/host/src/ble_hs_att_cmd.h
@@ -0,0 +1,58 @@
+/**
+ * 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 <inttypes.h>
+struct ble_l2cap_chan;
+
+#define BLE_HS_ATT_OP_ERROR_RSP     0x01
+#define BLE_HS_ATT_OP_READ_REQ      0x0a
+
+/**
+ * | Parameter                          | Size (octets) |
+ * +------------------------------------+---------------+
+ * | Attribute Opcode                   | 1             |
+ * | Attribute Handle                   | 2             |
+ */
+#define BLE_HS_ATT_READ_REQ_SZ      3
+struct ble_hs_att_read_req {
+    uint8_t bharq_op;
+    uint16_t bharq_handle;
+};
+
+/**
+ * | Parameter                          | Size (octets) |
+ * +------------------------------------+---------------+
+ * | Attribute Opcode                   | 1             |
+ * | Request Opcode In Error            | 1             |
+ * | Attribute Handle In Error          | 2             |
+ * | Error Code                         | 1             |
+ */
+#define BLE_HS_ATT_ERROR_RSP_SZ     5
+struct ble_hs_att_error_rsp {
+    uint8_t bhaep_op;
+    uint8_t bhaep_req_op;
+    uint16_t bhaep_handle;
+    uint8_t bhaep_error_code;
+};
+
+int ble_hs_att_read_req_parse(void *payload, int len,
+                              struct ble_hs_att_read_req *req);
+int ble_hs_att_read_req_write(void *payload, int len,
+                              struct ble_hs_att_read_req *req);
+int ble_hs_att_error_rsp_parse(void *payload, int len,
+                               struct ble_hs_att_error_rsp *rsp);
+int ble_hs_att_error_rsp_write(void *payload, int len,
+                               struct ble_hs_att_error_rsp *rsp);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_hs_itf.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_itf.h b/net/nimble/host/src/ble_hs_itf.h
index 12d2887..9f2dba5 100644
--- a/net/nimble/host/src/ble_hs_itf.h
+++ b/net/nimble/host/src/ble_hs_itf.h
@@ -17,11 +17,8 @@
 #ifndef H_ITF_
 #define H_ITF_
 
-struct ble_att_chan;
 struct os_eventq;
 
-int ble_att_chan_open_default(struct ble_att_chan *ac);
-int ble_att_chan_poll(struct ble_att_chan *c, struct os_eventq *evq);
 int ble_host_sim_send_data_connectionless(uint16_t con_handle, uint16_t cid,
                                           uint8_t *data, uint16_t len);
 int ble_sim_listen(uint16_t con_handle);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_l2cap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap.c b/net/nimble/host/src/ble_l2cap.c
index 7ee3cf0..cdacda9 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -112,7 +112,7 @@ ble_l2cap_write_hdr(void *dst, uint16_t len,
     return 0;
 }
 
-static struct ble_l2cap_chan *
+struct ble_l2cap_chan *
 ble_l2cap_chan_find(struct ble_hs_conn *conn, uint16_t cid)
 {
     struct ble_l2cap_chan *chan;
@@ -126,6 +126,53 @@ ble_l2cap_chan_find(struct ble_hs_conn *conn, uint16_t cid)
     return NULL;
 }
 
+/**
+ * If the specified pointer points to null, this function attempts to allocate
+ * an mbuf from the l2cap mbuf pool and assigns the result to the pointer.  No
+ * effect if the specified pointer already points to an mbuf.
+ *
+ * @return                      0 on success;
+ *                              ENOMEM on mbuf allocation failure.
+ */
+static int
+ble_l2cap_ensure_buf(struct os_mbuf **om)
+{
+    if (*om == NULL) {
+        *om = os_mbuf_get(&ble_l2cap_mbuf_pool, 0);
+        if (*om == NULL) {
+            return ENOMEM;
+        }
+    }
+
+    return 0;
+}
+
+int
+ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                     void *payload, int len)
+{
+    int rc;
+
+    rc = ble_l2cap_ensure_buf(&chan->blc_rx_buf);
+    if (rc != 0) {
+        /* XXX Need to deal with this in a way that prevents starvation. */
+        return ENOMEM;
+    }
+
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, chan->blc_rx_buf, payload, len);
+    if (rc != 0) {
+        /* XXX Need to deal with this in a way that prevents starvation. */
+        return rc;
+    }
+
+    rc = chan->blc_rx_fn(conn, chan);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
 int
 ble_l2cap_rx(struct ble_hs_conn *conn,
              struct hci_data_hdr *hci_hdr,
@@ -152,26 +199,33 @@ ble_l2cap_rx(struct ble_hs_conn *conn,
         return ENOENT;
     }
 
-    if (chan->blc_rx_buf == NULL) {
-        chan->blc_rx_buf = os_mbuf_get(&ble_l2cap_mbuf_pool, 0);
-        if (chan->blc_rx_buf == NULL) {
-            /* XXX Need to deal with this in a way that prevents starvation. */
-            return ENOMEM;
-        }
+    rc = ble_l2cap_rx_payload(conn, chan, u8ptr + BLE_L2CAP_HDR_SZ,
+                              l2cap_hdr.blh_len);
+    if (rc != 0) {
+        return rc;
     }
 
-    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, chan->blc_rx_buf,
-                        u8ptr + BLE_L2CAP_HDR_SZ, l2cap_hdr.blh_len);
+    return 0;
+}
+
+int
+ble_l2cap_tx(struct ble_l2cap_chan *chan, void *payload, int len)
+{
+    int rc;
+
+    rc = ble_l2cap_ensure_buf(&chan->blc_tx_buf);
     if (rc != 0) {
         /* XXX Need to deal with this in a way that prevents starvation. */
-        return rc;
+        return ENOMEM;
     }
 
-    rc = chan->blc_rx_fn(conn, chan);
+    rc = os_mbuf_append(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, payload, len);
     if (rc != 0) {
         return rc;
     }
 
+    /* XXX: L2CAP kick. */
+
     return 0;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/ble_l2cap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap.h b/net/nimble/host/src/ble_l2cap.h
index 7365ce7..e71d8a7 100644
--- a/net/nimble/host/src/ble_l2cap.h
+++ b/net/nimble/host/src/ble_l2cap.h
@@ -28,8 +28,6 @@ struct hci_data_hdr;
 
 #define BLE_L2CAP_HDR_SZ    4
 
-#define BLE_L2CAP_CHAN_BUF_CAP      256
-
 struct ble_l2cap_hdr
 {
     uint16_t blh_len;
@@ -37,19 +35,22 @@ struct ble_l2cap_hdr
 };
 
 struct ble_l2cap_chan;
+
 typedef int ble_l2cap_rx_fn(struct ble_hs_conn *conn,
                             struct ble_l2cap_chan *chan);
 
+typedef int ble_l2cap_tx_fn(struct ble_hs_conn *conn,
+                            struct ble_l2cap_chan *chan);
+
 struct ble_l2cap_chan
 {
     SLIST_ENTRY(ble_l2cap_chan) blc_next;
     uint16_t blc_cid;
-    ble_l2cap_rx_fn *blc_rx_fn;
 
     struct os_mbuf *blc_rx_buf;
+    struct os_mbuf *blc_tx_buf;
 
-    // tx mbuf
-    // tx callback
+    ble_l2cap_rx_fn *blc_rx_fn;
 };
 
 
@@ -58,15 +59,20 @@ SLIST_HEAD(ble_l2cap_chan_list, ble_l2cap_chan);
 struct ble_l2cap_chan *ble_l2cap_chan_alloc(void);
 void ble_l2cap_chan_free(struct ble_l2cap_chan *chan);
 
+struct ble_l2cap_chan *ble_l2cap_chan_find(struct ble_hs_conn *conn,
+                                           uint16_t cid);
 
 int ble_l2cap_parse_hdr(void *pkt, uint16_t len,
                         struct ble_l2cap_hdr *l2cap_hdr);
 int ble_l2cap_write_hdr(void *dst, uint16_t len,
                         const struct ble_l2cap_hdr *l2cap_hdr);
 
+int ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                         void *payload, int len);
 int ble_l2cap_rx(struct ble_hs_conn *connection,
                  struct hci_data_hdr *hci_hdr,
                  void *pkt);
+int ble_l2cap_tx(struct ble_l2cap_chan *chan, void *payload, int len);
 
 int ble_l2cap_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/test/ble_hs_att_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_att_test.c b/net/nimble/host/src/test/ble_hs_att_test.c
new file mode 100644
index 0000000..0243ffd
--- /dev/null
+++ b/net/nimble/host/src/test/ble_hs_att_test.c
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include "nimble/hci_common.h"
+#include "host/ble_hs.h"
+#include "host/ble_hs_test.h"
+#include "ble_l2cap.h"
+#include "ble_hs_conn.h"
+#include "ble_hs_att_cmd.h"
+#include "testutil/testutil.h"
+
+static void
+ble_hs_att_test_misc_verify_err_rsp(struct ble_l2cap_chan *chan,
+                                    uint8_t req_op, uint16_t handle,
+                                    uint8_t error_code)
+{
+    struct ble_hs_att_error_rsp rsp;
+    uint8_t buf[BLE_HS_ATT_ERROR_RSP_SZ];
+    int rc;
+
+    rc = os_mbuf_copydata(chan->blc_tx_buf, 0, sizeof buf, buf);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_hs_att_error_rsp_parse(buf, sizeof buf, &rsp);
+    TEST_ASSERT(rc == 0);
+
+    TEST_ASSERT(rsp.bhaep_op == BLE_HS_ATT_OP_ERROR_RSP);
+    TEST_ASSERT(rsp.bhaep_req_op == req_op);
+    TEST_ASSERT(rsp.bhaep_handle == handle);
+    TEST_ASSERT(rsp.bhaep_error_code == error_code);
+}
+
+TEST_CASE(ble_hs_att_test_small_read)
+{
+    struct ble_hs_att_read_req req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
+    int rc;
+
+    conn = ble_hs_conn_alloc();
+    TEST_ASSERT_FATAL(conn != NULL);
+
+    chan = ble_l2cap_chan_find(conn, BLE_L2CAP_CID_ATT);
+    TEST_ASSERT_FATAL(chan != NULL);
+
+    /*** Nonexistent attribute. */
+    req.bharq_op = BLE_HS_ATT_OP_READ_REQ;
+    req.bharq_handle = 0;
+    rc = ble_hs_att_read_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_l2cap_rx_payload(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_hs_att_test_misc_verify_err_rsp(chan, BLE_HS_ATT_OP_READ_REQ, 0,
+                                        BLE_ERR_ATTR_NOT_FOUND);
+
+    ble_hs_conn_free(conn);
+}
+
+TEST_SUITE(att_suite)
+{
+    int rc;
+
+    rc = host_init();
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_att_test_small_read();
+}
+
+int
+ble_hs_att_test_all(void)
+{
+    att_suite();
+
+    return tu_any_failed;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/test/ble_hs_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test.c b/net/nimble/host/src/test/ble_hs_test.c
index 79b621f..a2ba713 100644
--- a/net/nimble/host/src/test/ble_hs_test.c
+++ b/net/nimble/host/src/test/ble_hs_test.c
@@ -1,65 +1,22 @@
-#include <stddef.h>
-#include <errno.h>
-#include "nimble/hci_common.h"
-#include "host/ble_hs.h"
+/**
+ * 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 "host/ble_hs_test.h"
-#include "ble_hs_conn.h"
-#include "ble_l2cap.h"
 #include "testutil/testutil.h"
 
-TEST_CASE(l2cap_test_bad_header)
-{
-    struct ble_l2cap_hdr l2cap_hdr;
-    struct hci_data_hdr hci_hdr;
-    struct ble_hs_conn *conn;
-    uint8_t pkt[8];
-    int rc;
-
-    conn = ble_hs_conn_alloc();
-    TEST_ASSERT_FATAL(conn != NULL);
-
-    hci_hdr.hdh_handle_pb_bc = 0;
-    hci_hdr.hdh_len = 10;
-
-    /* HCI header indicates a length of 10, but L2CAP header has a length
-     * of 0.
-     */
-    l2cap_hdr.blh_len = 0;
-    l2cap_hdr.blh_cid = 0;
-    rc = ble_l2cap_write_hdr(pkt, sizeof pkt, &l2cap_hdr);
-    TEST_ASSERT(rc == 0);
-    rc = ble_l2cap_rx(conn, &hci_hdr, pkt);
-    TEST_ASSERT(rc == EMSGSIZE);
-
-    /* Length is correct; specified channel doesn't exist. */
-    l2cap_hdr.blh_len = 6;
-    l2cap_hdr.blh_cid = 0;
-    rc = ble_l2cap_write_hdr(pkt, sizeof pkt, &l2cap_hdr);
-    TEST_ASSERT(rc == 0);
-    rc = ble_l2cap_rx(conn, &hci_hdr, pkt);
-    TEST_ASSERT(rc == ENOENT);
-
-    ble_hs_conn_free(conn);
-}
-
-TEST_SUITE(l2cap_gen)
-{
-    int rc;
-
-    rc = host_init();
-    TEST_ASSERT_FATAL(rc == 0);
-
-    l2cap_test_bad_header();
-}
-
-int
-l2cap_test_all(void)
-{
-    l2cap_gen();
-
-    return tu_any_failed;
-}
-
 #ifdef PKG_TEST
 
 int
@@ -69,9 +26,9 @@ main(void)
     tu_init();
 
     l2cap_test_all();
+    ble_hs_att_test_all();
 
     return tu_any_failed;
 }
 
 #endif
-

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/34d3b2e5/net/nimble/host/src/test/ble_l2cap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_l2cap_test.c b/net/nimble/host/src/test/ble_l2cap_test.c
new file mode 100644
index 0000000..e167095
--- /dev/null
+++ b/net/nimble/host/src/test/ble_l2cap_test.c
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2015 Runtime Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include "nimble/hci_common.h"
+#include "host/ble_hs.h"
+#include "host/ble_hs_test.h"
+#include "ble_hs_conn.h"
+#include "ble_l2cap.h"
+#include "testutil/testutil.h"
+
+TEST_CASE(l2cap_test_bad_header)
+{
+    struct ble_l2cap_hdr l2cap_hdr;
+    struct hci_data_hdr hci_hdr;
+    struct ble_hs_conn *conn;
+    uint8_t pkt[8];
+    int rc;
+
+    conn = ble_hs_conn_alloc();
+    TEST_ASSERT_FATAL(conn != NULL);
+
+    hci_hdr.hdh_handle_pb_bc = 0;
+    hci_hdr.hdh_len = 10;
+
+    /* HCI header indicates a length of 10, but L2CAP header has a length
+     * of 0.
+     */
+    l2cap_hdr.blh_len = 0;
+    l2cap_hdr.blh_cid = 0;
+    rc = ble_l2cap_write_hdr(pkt, sizeof pkt, &l2cap_hdr);
+    TEST_ASSERT(rc == 0);
+    rc = ble_l2cap_rx(conn, &hci_hdr, pkt);
+    TEST_ASSERT(rc == EMSGSIZE);
+
+    /* Length is correct; specified channel doesn't exist. */
+    l2cap_hdr.blh_len = 6;
+    l2cap_hdr.blh_cid = 0;
+    rc = ble_l2cap_write_hdr(pkt, sizeof pkt, &l2cap_hdr);
+    TEST_ASSERT(rc == 0);
+    rc = ble_l2cap_rx(conn, &hci_hdr, pkt);
+    TEST_ASSERT(rc == ENOENT);
+
+    ble_hs_conn_free(conn);
+}
+
+TEST_SUITE(l2cap_gen)
+{
+    int rc;
+
+    rc = host_init();
+    TEST_ASSERT_FATAL(rc == 0);
+
+    l2cap_test_bad_header();
+}
+
+int
+l2cap_test_all(void)
+{
+    l2cap_gen();
+
+    return tu_any_failed;
+}
+