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/07 00:37:47 UTC

[1/2] incubator-mynewt-larva git commit: Fix mbuf bug: zero out pkthdr when allocated.

Repository: incubator-mynewt-larva
Updated Branches:
  refs/heads/master ed00b2faa -> 98a83fa71


Fix mbuf bug: zero out pkthdr when allocated.


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

Branch: refs/heads/master
Commit: 7d8b86b4659042070b6a1ceb707c3454cb5f5377
Parents: ed00b2f
Author: Christopher Collins <cc...@gmail.com>
Authored: Fri Nov 6 15:36:52 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Fri Nov 6 15:36:52 2015 -0800

----------------------------------------------------------------------
 libs/os/src/os_mbuf.c | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/7d8b86b4/libs/os/src/os_mbuf.c
----------------------------------------------------------------------
diff --git a/libs/os/src/os_mbuf.c b/libs/os/src/os_mbuf.c
index d6710d3..f78d373 100644
--- a/libs/os/src/os_mbuf.c
+++ b/libs/os/src/os_mbuf.c
@@ -111,12 +111,16 @@ err:
 struct os_mbuf *
 os_mbuf_get_pkthdr(struct os_mbuf_pool *omp)
 {
+    struct os_mbuf_pkthdr *pkthdr;
     struct os_mbuf *om;
 
     om = os_mbuf_get(omp, 0);
     if (om) {
         om->om_flags |= OS_MBUF_F_MASK(OS_MBUF_F_PKTHDR);
         om->om_data += omp->omp_hdr_len + sizeof(struct os_mbuf_pkthdr);
+
+        pkthdr = OS_MBUF_PKTHDR(om);
+        pkthdr->omp_len = 0;
     }
 
     return om;


[2/2] incubator-mynewt-larva git commit: Basic ATT write / ATT MTU exchange handling.

Posted by cc...@apache.org.
Basic ATT write / ATT MTU exchange handling.


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

Branch: refs/heads/master
Commit: 98a83fa7198599f32e92aa18862f0fb7b872fc02
Parents: 7d8b86b
Author: Christopher Collins <cc...@gmail.com>
Authored: Fri Nov 6 15:37:24 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Fri Nov 6 15:37:24 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/src/ble_hs_att.c           | 133 +++++++++++++++++++++---
 net/nimble/host/src/ble_hs_att.h           |  20 ++--
 net/nimble/host/src/ble_hs_att_cmd.c       | 114 +++++++++++++++++---
 net/nimble/host/src/ble_hs_att_cmd.h       |  71 +++++++++++--
 net/nimble/host/src/ble_l2cap.c            |  10 +-
 net/nimble/host/src/ble_l2cap.h            |   3 +-
 net/nimble/host/src/test/ble_hs_att_test.c | 103 ++++++++++++++++--
 7 files changed, 396 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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 2be795d..1d04748 100644
--- a/net/nimble/host/src/ble_hs_att.c
+++ b/net/nimble/host/src/ble_hs_att.c
@@ -26,25 +26,34 @@
 #include "ble_hs_att.h"
 
 typedef int ble_hs_att_rx_fn(struct ble_hs_conn *conn,
-                             struct ble_l2cap_chan *chan);
+                             struct ble_l2cap_chan *chan,
+                             struct os_mbuf *om);
 struct ble_hs_att_rx_dispatch_entry {
     uint8_t bde_op;
     ble_hs_att_rx_fn *bde_fn;
 };
 
 /** 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);
+static int ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn,
+                                 struct ble_l2cap_chan *chan,
+                                 struct os_mbuf *om);
+static int ble_hs_att_rx_read_req(struct ble_hs_conn *conn,
+                                  struct ble_l2cap_chan *chan,
+                                  struct os_mbuf *om);
+static int ble_hs_att_rx_write_req(struct ble_hs_conn *conn,
+                                   struct ble_l2cap_chan *chan,
+                                   struct os_mbuf *om);
 
 struct ble_hs_att_rx_dispatch_entry ble_hs_att_rx_dispatch[] = {
-    { BLE_HS_ATT_OP_READ_REQ, ble_hs_att_rx_read },
+    { BLE_HS_ATT_OP_MTU_REQ,    ble_hs_att_rx_mtu_req },
+    { BLE_HS_ATT_OP_READ_REQ,   ble_hs_att_rx_read_req },
+    { BLE_HS_ATT_OP_WRITE_REQ,  ble_hs_att_rx_write_req },
 };
 
 #define BLE_HS_ATT_RX_DISPATCH_SZ \
     (sizeof ble_hs_att_rx_dispatch / sizeof ble_hs_att_rx_dispatch[0])
 
-static STAILQ_HEAD(, ble_hs_att_entry) g_ble_hs_att_list =
-    STAILQ_HEAD_INITIALIZER(g_ble_hs_att_list);
+static STAILQ_HEAD(, ble_hs_att_entry) g_ble_hs_att_list;
 static uint16_t g_ble_hs_att_id;
 
 static struct os_mutex g_ble_hs_att_list_mutex;
@@ -348,6 +357,28 @@ ble_hs_att_tx_error_rsp(struct ble_l2cap_chan *chan, uint8_t req_op,
 }
 
 static int
+ble_hs_att_rx_mtu_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                      struct os_mbuf *om)
+{
+    struct ble_hs_att_mtu_req req;
+    uint8_t buf[BLE_HS_ATT_MTU_REQ_SZ];
+    int rc;
+
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_hs_att_mtu_req_parse(buf, sizeof buf, &req);
+    assert(rc == 0);
+
+    /* XXX: Update connection MTU. */
+    /* XXX: Send response. */
+
+    return 0;
+}
+
+static int
 ble_hs_att_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                        void *attr_data, int attr_len)
 {
@@ -379,21 +410,19 @@ ble_hs_att_tx_read_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 }
 
 static int
-ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+ble_hs_att_rx_read_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                       struct os_mbuf *om)
 {
+    union ble_hs_att_handle_arg arg;
     struct ble_hs_att_read_req req;
     struct ble_hs_att_entry *entry;
     uint8_t buf[BLE_HS_ATT_READ_REQ_SZ];
-    uint8_t *attr_data;
-    int attr_len;
     int rc;
 
-    rc = os_mbuf_copydata(chan->blc_rx_buf, 0, sizeof buf, buf);
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
     if (rc != 0) {
         return rc;
     }
-    /* Strip ATT read request from the buffer. */
-    ble_l2cap_strip(chan, BLE_HS_ATT_READ_REQ_SZ);
 
     rc = ble_hs_att_read_req_parse(buf, sizeof buf, &req);
     assert(rc == 0);
@@ -408,13 +437,14 @@ ble_hs_att_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
         goto send_err;
     }
 
-    rc = entry->ha_fn(entry, BLE_HS_ATT_OP_READ_REQ, &attr_data, &attr_len);
+    rc = entry->ha_fn(entry, BLE_HS_ATT_OP_READ_REQ, &arg);
     if (rc != 0) {
         rc = BLE_ERR_UNSPECIFIED;
         goto send_err;
     }
 
-    rc = ble_hs_att_tx_read_rsp(conn, chan, attr_data, attr_len);
+    rc = ble_hs_att_tx_read_rsp(conn, chan, arg.aha_read.attr_data,
+                                arg.aha_read.attr_len);
     if (rc != 0) {
         goto send_err;
     }
@@ -428,13 +458,80 @@ send_err:
 }
 
 static int
-ble_hs_att_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+ble_hs_att_tx_write_rsp(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+{
+    uint8_t op;
+    int rc;
+
+    op = BLE_HS_ATT_OP_WRITE_RSP;
+    rc = ble_l2cap_tx(chan, &op, 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* XXX: Kick L2CAP. */
+
+    return 0;
+}
+
+static int
+ble_hs_att_rx_write_req(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                        struct os_mbuf *om)
+{
+    union ble_hs_att_handle_arg arg;
+    struct ble_hs_att_write_req req;
+    struct ble_hs_att_entry *entry;
+    uint8_t buf[BLE_HS_ATT_WRITE_REQ_MIN_SZ];
+    int rc;
+
+    rc = os_mbuf_copydata(om, 0, sizeof buf, buf);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_hs_att_write_req_parse(buf, sizeof buf, &req);
+    assert(rc == 0);
+
+    rc = ble_hs_att_find_by_handle(req.bhawq_handle, &entry);
+    if (rc != 0) {
+        goto send_err;
+    }
+
+    if (entry->ha_fn == NULL) {
+        rc = BLE_ERR_UNSPECIFIED;
+        goto send_err;
+    }
+
+    arg.aha_write.om = om;
+    arg.aha_write.attr_len = OS_MBUF_PKTHDR(om)->omp_len;
+    rc = entry->ha_fn(entry, BLE_HS_ATT_OP_WRITE_REQ, &arg);
+    if (rc != 0) {
+        rc = BLE_ERR_UNSPECIFIED;
+        goto send_err;
+    }
+
+    rc = ble_hs_att_tx_write_rsp(conn, chan);
+    if (rc != 0) {
+        goto send_err;
+    }
+
+    return 0;
+
+send_err:
+    ble_hs_att_tx_error_rsp(chan, BLE_HS_ATT_OP_WRITE_REQ,
+                            req.bhawq_handle, rc);
+    return rc;
+}
+
+static int
+ble_hs_att_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+              struct os_mbuf *om)
 {
     struct ble_hs_att_rx_dispatch_entry *entry;
     uint8_t op;
     int rc;
 
-    rc = os_mbuf_copydata(chan->blc_rx_buf, 0, 1, &op);
+    rc = os_mbuf_copydata(om, 0, 1, &op);
     if (rc != 0) {
         return EMSGSIZE;
     }
@@ -444,7 +541,7 @@ ble_hs_att_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
         return EINVAL;
     }
 
-    rc = entry->bde_fn(conn, chan);
+    rc = entry->bde_fn(conn, chan, om);
     if (rc != 0) {
         return rc;
     }
@@ -473,6 +570,8 @@ ble_hs_att_init(void)
 {
     int rc;
 
+    STAILQ_INIT(&g_ble_hs_att_list);
+
     rc = os_mutex_init(&g_ble_hs_att_list_mutex);
     if (rc != 0) {
         return rc;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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 573b260..7373199 100644
--- a/net/nimble/host/src/ble_hs_att.h
+++ b/net/nimble/host/src/ble_hs_att.h
@@ -23,6 +23,18 @@
 
 struct ble_hs_att_entry;
 
+union ble_hs_att_handle_arg {
+    struct {
+        void *attr_data;
+        int attr_len;
+    } aha_read;
+
+    struct {
+        struct os_mbuf *om;
+        int attr_len;
+    } aha_write;
+};
+
 /**
  * Called from ble_hs_att_walk().  Called on each entry in the 
  * ble_hs_att_list.
@@ -41,7 +53,8 @@ typedef int (*ble_hs_att_walk_func_t)(struct ble_hs_att_entry *, void *arg);
  * @param The request data associated with that host attribute
  */
 typedef int ble_hs_att_handle_func(struct ble_hs_att_entry *entry,
-                                   uint8_t op, uint8_t **data, int *len);
+                                   uint8_t op,
+                                   union ble_hs_att_handle_arg *arg);
 
 #define HA_FLAG_PERM_READ            (1 << 0)
 #define HA_FLAG_PERM_WRITE           (1 << 1) 
@@ -65,11 +78,6 @@ struct ble_hs_att_entry {
 #define HA_OPCODE_COMMAND_FLAG (1 << 6) 
 #define HA_OPCODE_AUTH_SIG_FLAG (1 << 7) 
 
-
-#define HA_METH_ERROR_RSP        (0x01)
-#define HA_METH_EXCHANGE_MTU_REQ (0x02)
-#define HA_METH_EXCHANGE_MTU_RSP (0x03)
-
 int ble_hs_att_register(uint8_t *uuid, uint8_t flags, uint16_t *handle_id,
                         ble_hs_att_handle_func *fn);
 struct ble_l2cap_chan *ble_hs_att_create_chan(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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
index ea4f59d..aba7559 100644
--- a/net/nimble/host/src/ble_hs_att_cmd.c
+++ b/net/nimble/host/src/ble_hs_att_cmd.c
@@ -6,6 +6,94 @@
 #include "ble_hs_att_cmd.h"
 
 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;
+}
+
+int
+ble_hs_att_mtu_req_parse(void *payload, int len,
+                         struct ble_hs_att_mtu_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_MTU_REQ_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    req->bhamq_op = u8ptr[0];
+    req->bhamq_mtu = le16toh(u8ptr + 1);
+
+    if (req->bhamq_op != BLE_HS_ATT_OP_MTU_REQ) {
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+int
+ble_hs_att_mtu_rsp_parse(void *payload, int len,
+                         struct ble_hs_att_mtu_rsp *rsp)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_HS_ATT_MTU_RSP_SZ) {
+        return EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    rsp->bhamp_op = u8ptr[0];
+    rsp->bhamp_mtu = le16toh(u8ptr + 1);
+
+    if (rsp->bhamp_op != BLE_HS_ATT_OP_MTU_RSP) {
+        return EINVAL;
+    }
+
+    return 0;
+}
+
+int
 ble_hs_att_read_req_parse(void *payload, int len,
                           struct ble_hs_att_read_req *req)
 {
@@ -46,23 +134,21 @@ ble_hs_att_read_req_write(void *payload, int len,
 }
 
 int
-ble_hs_att_error_rsp_parse(void *payload, int len,
-                           struct ble_hs_att_error_rsp *rsp)
+ble_hs_att_write_req_parse(void *payload, int len,
+                           struct ble_hs_att_write_req *req)
 {
     uint8_t *u8ptr;
 
-    if (len < BLE_HS_ATT_ERROR_RSP_SZ) {
+    if (len < BLE_HS_ATT_WRITE_REQ_MIN_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];
+    req->bhawq_op = u8ptr[0];
+    req->bhawq_handle = le16toh(u8ptr + 1);
 
-    if (rsp->bhaep_op != BLE_HS_ATT_OP_ERROR_RSP) {
+    if (req->bhawq_op != BLE_HS_ATT_OP_WRITE_REQ) {
         return EINVAL;
     }
 
@@ -70,21 +156,19 @@ ble_hs_att_error_rsp_parse(void *payload, int len,
 }
 
 int
-ble_hs_att_error_rsp_write(void *payload, int len,
-                           struct ble_hs_att_error_rsp *rsp)
+ble_hs_att_write_req_write(void *payload, int len,
+                           struct ble_hs_att_write_req *req)
 {
     uint8_t *u8ptr;
 
-    if (len < BLE_HS_ATT_ERROR_RSP_SZ) {
+    if (len < BLE_HS_ATT_WRITE_REQ_MIN_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;
+    u8ptr[0] = req->bhawq_op;
+    htole16(u8ptr + 1, req->bhawq_handle);
 
     return 0;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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
index a06e75f..3306e6f 100644
--- a/net/nimble/host/src/ble_hs_att_cmd.h
+++ b/net/nimble/host/src/ble_hs_att_cmd.h
@@ -17,9 +17,13 @@
 #include <inttypes.h>
 struct ble_l2cap_chan;
 
-#define BLE_HS_ATT_OP_ERROR_RSP     0x01
-#define BLE_HS_ATT_OP_READ_REQ      0x0a
-#define BLE_HS_ATT_OP_READ_RSP      0x0b
+#define BLE_HS_ATT_OP_ERROR_RSP             0x01
+#define BLE_HS_ATT_OP_MTU_REQ               0x02
+#define BLE_HS_ATT_OP_MTU_RSP               0x03
+#define BLE_HS_ATT_OP_READ_REQ              0x0a
+#define BLE_HS_ATT_OP_READ_RSP              0x0b
+#define BLE_HS_ATT_OP_WRITE_REQ             0x12
+#define BLE_HS_ATT_OP_WRITE_RSP             0x13
 
 /**
  * | Parameter                          | Size (octets)     |
@@ -27,7 +31,7 @@ struct ble_l2cap_chan;
  * | Attribute Opcode                   | 1                 |
  * | Attribute Handle                   | 2                 |
  */
-#define BLE_HS_ATT_READ_REQ_SZ      3
+#define BLE_HS_ATT_READ_REQ_SZ              3
 struct ble_hs_att_read_req {
     uint8_t bharq_op;
     uint16_t bharq_handle;
@@ -39,7 +43,7 @@ struct ble_hs_att_read_req {
  * | Attribute Opcode                   | 1                 |
  * | Attribute Value                    | 0 to (ATT_MTU-1)  |
  */
-#define BLE_HS_ATT_READ_RSP_MIN_SZ      1
+#define BLE_HS_ATT_READ_RSP_MIN_SZ          1
 
 /**
  * | Parameter                          | Size (octets)     |
@@ -49,7 +53,7 @@ struct ble_hs_att_read_req {
  * | Attribute Handle In Error          | 2                 |
  * | Error Code                         | 1                 |
  */
-#define BLE_HS_ATT_ERROR_RSP_SZ     5
+#define BLE_HS_ATT_ERROR_RSP_SZ             5
 struct ble_hs_att_error_rsp {
     uint8_t bhaep_op;
     uint8_t bhaep_req_op;
@@ -57,11 +61,58 @@ struct ble_hs_att_error_rsp {
     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);
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Client Rx MTU                      | 2                 |
+ */
+#define BLE_HS_ATT_MTU_REQ_SZ               3
+struct ble_hs_att_mtu_req {
+    uint8_t bhamq_op;
+    uint16_t bhamq_mtu;
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Server Rx MTU                      | 2                 |
+ */
+#define BLE_HS_ATT_MTU_RSP_SZ               3
+struct ble_hs_att_mtu_rsp {
+    uint8_t bhamp_op;
+    uint16_t bhamp_mtu;
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Attribute Handle                   | 2                 |
+ * | Attribute Value                    | 0 to (ATT_MTU-3)  |
+ */
+#define BLE_HS_ATT_WRITE_REQ_MIN_SZ         3
+struct ble_hs_att_write_req {
+    uint8_t bhawq_op;
+    uint16_t bhawq_handle;
+};
+
+#define BLE_HS_ATT_WRITE_RSP_SZ             1
+
 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);
+int ble_hs_att_mtu_req_parse(void *payload, int len,
+                             struct ble_hs_att_mtu_req *req);
+int ble_hs_att_mtu_rsp_parse(void *payload, int len,
+                             struct ble_hs_att_mtu_rsp *rsp);
+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_write_req_parse(void *payload, int len,
+                               struct ble_hs_att_write_req *req);
+int ble_hs_att_write_req_write(void *payload, int len,
+                               struct ble_hs_att_write_req *req);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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 9c85c8c..6ec8b10 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -138,7 +138,7 @@ static int
 ble_l2cap_ensure_buf(struct os_mbuf **om)
 {
     if (*om == NULL) {
-        *om = os_mbuf_get(&ble_l2cap_mbuf_pool, 0);
+        *om = os_mbuf_get_pkthdr(&ble_l2cap_mbuf_pool);
         if (*om == NULL) {
             return ENOMEM;
         }
@@ -151,6 +151,7 @@ int
 ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                      void *payload, int len)
 {
+    struct os_mbuf *om;
     int rc;
 
     rc = ble_l2cap_ensure_buf(&chan->blc_rx_buf);
@@ -165,7 +166,12 @@ ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         return rc;
     }
 
-    rc = chan->blc_rx_fn(conn, chan);
+    /* XXX: Check that complete SDU has been received. */
+    om = chan->blc_rx_buf;
+    chan->blc_rx_buf = NULL;
+
+    rc = chan->blc_rx_fn(conn, chan, om);
+    os_mbuf_free_chain(&ble_l2cap_mbuf_pool, om);
     if (rc != 0) {
         return rc;
     }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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 e71d8a7..300cd69 100644
--- a/net/nimble/host/src/ble_l2cap.h
+++ b/net/nimble/host/src/ble_l2cap.h
@@ -37,7 +37,8 @@ struct ble_l2cap_hdr
 struct ble_l2cap_chan;
 
 typedef int ble_l2cap_rx_fn(struct ble_hs_conn *conn,
-                            struct ble_l2cap_chan *chan);
+                            struct ble_l2cap_chan *chan,
+                            struct os_mbuf *om);
 
 typedef int ble_l2cap_tx_fn(struct ble_hs_conn *conn,
                             struct ble_l2cap_chan *chan);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/98a83fa7/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
index 4450eeb..4717dee 100644
--- a/net/nimble/host/src/test/ble_hs_att_test.c
+++ b/net/nimble/host/src/test/ble_hs_att_test.c
@@ -29,14 +29,45 @@
 static uint8_t *ble_hs_att_test_attr_1;
 static int ble_hs_att_test_attr_1_len;
 
+static uint8_t ble_hs_att_test_attr_2[1024];
+static int ble_hs_att_test_attr_2_len;
+
 static int
 ble_hs_att_test_misc_attr_fn_1(struct ble_hs_att_entry *entry,
-                               uint8_t op, uint8_t **data, int *len)
+                               uint8_t op, union ble_hs_att_handle_arg *arg)
 {
-    *data = ble_hs_att_test_attr_1;
-    *len = ble_hs_att_test_attr_1_len;
+    switch (op) {
+    case BLE_HS_ATT_OP_READ_REQ:
+        arg->aha_read.attr_data = ble_hs_att_test_attr_1;
+        arg->aha_read.attr_len = ble_hs_att_test_attr_1_len;
+        return 0;
+
+    default:
+        return -1;
+    }
+}
+
+static int
+ble_hs_att_test_misc_attr_fn_2(struct ble_hs_att_entry *entry,
+                               uint8_t op, union ble_hs_att_handle_arg *arg)
+{
+    struct os_mbuf_pkthdr *omp;
+    int rc;
 
-    return 0;
+    switch (op) {
+    case BLE_HS_ATT_OP_WRITE_REQ:
+        omp = OS_MBUF_PKTHDR(arg->aha_write.om);
+        rc = os_mbuf_copydata(arg->aha_write.om, 0, arg->aha_write.attr_len,
+                              ble_hs_att_test_attr_2);
+        TEST_ASSERT(rc == 0);
+        ble_hs_att_test_attr_2_len = arg->aha_write.attr_len;
+        return 0;
+
+    default:
+        return -1;
+    }
+
+    (void)omp;
 }
 
 static void
@@ -89,6 +120,21 @@ ble_hs_att_test_misc_verify_tx_read_rsp(struct ble_l2cap_chan *chan,
     os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf, attr_len + 1);
 }
 
+static void
+ble_hs_att_test_misc_verify_tx_write_rsp(struct ble_l2cap_chan *chan)
+{
+    uint8_t u8;
+    int rc;
+
+    rc = os_mbuf_copydata(chan->blc_tx_buf, 0, 1, &u8);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(u8 == BLE_HS_ATT_OP_WRITE_RSP);
+
+    /* Remove the write response from the buffer. */
+    os_mbuf_adj(&ble_l2cap_mbuf_pool, chan->blc_tx_buf,
+                BLE_HS_ATT_WRITE_RSP_SZ);
+}
+
 TEST_CASE(ble_hs_att_test_read)
 {
     struct ble_hs_att_read_req req;
@@ -98,6 +144,9 @@ TEST_CASE(ble_hs_att_test_read)
     uint8_t uuid[16] = {0};
     int rc;
 
+    rc = host_init();
+    TEST_ASSERT_FATAL(rc == 0);
+
     conn = ble_hs_conn_alloc();
     TEST_ASSERT_FATAL(conn != NULL);
 
@@ -146,18 +195,58 @@ TEST_CASE(ble_hs_att_test_read)
 
     ble_hs_att_test_misc_verify_tx_read_rsp(chan, ble_hs_att_test_attr_1,
                                             BLE_HS_ATT_MTU_DFLT - 1);
-
-    ble_hs_conn_free(conn);
 }
 
-TEST_SUITE(att_suite)
+TEST_CASE(ble_hs_att_test_write)
 {
+    struct ble_hs_att_write_req req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    uint8_t buf[BLE_HS_ATT_READ_REQ_SZ + 8];
+    uint8_t uuid[16] = {0};
     int rc;
 
     rc = host_init();
     TEST_ASSERT_FATAL(rc == 0);
 
+    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.bhawq_op = BLE_HS_ATT_OP_WRITE_REQ;
+    req.bhawq_handle = 0;
+    rc = ble_hs_att_write_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    memcpy(buf + BLE_HS_ATT_READ_REQ_SZ, ((uint8_t[]){0,1,2,3,4,5,6,7}), 8);
+
+    rc = ble_l2cap_rx_payload(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc != 0);
+    ble_hs_att_test_misc_verify_tx_err_rsp(chan, BLE_HS_ATT_OP_WRITE_REQ, 0,
+                                           BLE_ERR_ATTR_NOT_FOUND);
+
+    /*** Successful write. */
+    rc = ble_hs_att_register(uuid, 0, &req.bhawq_handle,
+                             ble_hs_att_test_misc_attr_fn_2);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_hs_att_write_req_write(buf, sizeof buf, &req);
+    TEST_ASSERT(rc == 0);
+    memcpy(buf + BLE_HS_ATT_WRITE_REQ_MIN_SZ,
+           ((uint8_t[]){0,1,2,3,4,5,6,7}), 8);
+
+    rc = ble_l2cap_rx_payload(conn, chan, buf, sizeof buf);
+    TEST_ASSERT(rc == 0);
+
+    ble_hs_att_test_misc_verify_tx_write_rsp(chan);
+}
+
+TEST_SUITE(att_suite)
+{
     ble_hs_att_test_read();
+    ble_hs_att_test_write();
 }
 
 int