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/16 04:55:03 UTC

[3/3] incubator-mynewt-larva git commit: ATT prepare write and execute write commands.

ATT prepare write and execute write commands.


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

Branch: refs/heads/master
Commit: 762dccc393780191a5544a62f3b507535f1d6534
Parents: 27f8daf
Author: Christopher Collins <cc...@gmail.com>
Authored: Tue Dec 15 15:33:22 2015 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Tue Dec 15 19:54:24 2015 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_att.h      |  11 +-
 net/nimble/host/src/ble_att.c               |   2 +
 net/nimble/host/src/ble_att_cmd.c           | 132 +++++++
 net/nimble/host/src/ble_att_cmd.h           |  52 ++-
 net/nimble/host/src/ble_att_priv.h          |  19 +
 net/nimble/host/src/ble_att_svr.c           | 431 ++++++++++++++++++++++-
 net/nimble/host/src/ble_hs.c                |  27 +-
 net/nimble/host/src/ble_hs_conn.c           |   2 -
 net/nimble/host/src/ble_hs_conn.h           |   3 +-
 net/nimble/host/src/ble_hs_misc.c           |  23 ++
 net/nimble/host/src/ble_hs_priv.h           |   4 +
 net/nimble/host/src/test/ble_att_svr_test.c |  11 +-
 12 files changed, 664 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/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 c97f7f6..0b611da 100644
--- a/net/nimble/host/include/host/ble_att.h
+++ b/net/nimble/host/include/host/ble_att.h
@@ -25,7 +25,10 @@
 #define BLE_ATT_ERR_INVALID_HANDLE          0x01
 #define BLE_ATT_ERR_INVALID_PDU             0x04
 #define BLE_ATT_ERR_REQ_NOT_SUPPORTED       0x06
+#define BLE_ATT_ERR_INVALID_OFFSET          0x07
+#define BLE_ATT_ERR_PREPARE_QUEUE_FULL      0x09
 #define BLE_ATT_ERR_ATTR_NOT_FOUND          0x0a
+#define BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN  0x0d
 #define BLE_ATT_ERR_UNLIKELY                0x0e
 #define BLE_ATT_ERR_UNSUPPORTED_GROUP       0x10
 #define BLE_ATT_ERR_INSUFFICIENT_RES        0x11
@@ -45,8 +48,14 @@
 #define BLE_ATT_OP_READ_GROUP_TYPE_RSP      0x11
 #define BLE_ATT_OP_WRITE_REQ                0x12
 #define BLE_ATT_OP_WRITE_RSP                0x13
+#define BLE_ATT_OP_PREP_WRITE_REQ           0x16
+#define BLE_ATT_OP_PREP_WRITE_RSP           0x17
+#define BLE_ATT_OP_EXEC_WRITE_REQ           0x18
+#define BLE_ATT_OP_EXEC_WRITE_RSP           0x19
 #define BLE_ATT_OP_WRITE_CMD                0x52
 
+#define BLE_ATT_ATTR_MAX_LEN                512
+
 union ble_att_svr_handle_arg {
     struct {
         void *attr_data;
@@ -54,7 +63,7 @@ union ble_att_svr_handle_arg {
     } aha_read;
 
     struct {
-        struct os_mbuf *om;
+        void *attr_data;
         int attr_len;
     } aha_write;
 };

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_att.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att.c b/net/nimble/host/src/ble_att.c
index b308ccb..a90a6da 100644
--- a/net/nimble/host/src/ble_att.c
+++ b/net/nimble/host/src/ble_att.c
@@ -46,6 +46,8 @@ static struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = {
     { BLE_ATT_OP_READ_GROUP_TYPE_RSP,  ble_att_clt_rx_read_group_type },
     { BLE_ATT_OP_WRITE_REQ,            ble_att_svr_rx_write },
     { BLE_ATT_OP_WRITE_RSP,            ble_att_clt_rx_write },
+    { BLE_ATT_OP_PREP_WRITE_REQ,       ble_att_svr_rx_prep_write },
+    { BLE_ATT_OP_EXEC_WRITE_REQ,       ble_att_svr_rx_exec_write },
 };
 
 #define BLE_ATT_RX_DISPATCH_SZ \

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_att_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_cmd.c b/net/nimble/host/src/ble_att_cmd.c
index 6b9a781..ce6f364 100644
--- a/net/nimble/host/src/ble_att_cmd.c
+++ b/net/nimble/host/src/ble_att_cmd.c
@@ -511,3 +511,135 @@ ble_att_write_cmd_write(void *payload, int len, struct ble_att_write_req *req)
 
     return 0;
 }
+
+static int
+ble_att_prep_write_cmd_parse(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd, int is_req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_ATT_PREP_WRITE_CMD_BASE_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    if (is_req) {
+        if (u8ptr[0] != BLE_ATT_OP_PREP_WRITE_REQ) {
+            return BLE_HS_EINVAL;
+        }
+    } else {
+        if (u8ptr[0] != BLE_ATT_OP_PREP_WRITE_RSP) {
+            return BLE_HS_EINVAL;
+        }
+    }
+
+    cmd->bapc_handle = le16toh(u8ptr + 1);
+    cmd->bapc_offset = le16toh(u8ptr + 3);
+
+    return 0;
+}
+
+static int
+ble_att_prep_write_cmd_write(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd, int is_req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_ATT_PREP_WRITE_CMD_BASE_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    if (is_req) {
+        u8ptr[0] = BLE_ATT_OP_PREP_WRITE_REQ;
+    } else {
+        u8ptr[0] = BLE_ATT_OP_PREP_WRITE_RSP;
+    }
+    htole16(u8ptr + 1, cmd->bapc_handle);
+    htole16(u8ptr + 3, cmd->bapc_offset);
+
+    return 0;
+}
+
+int
+ble_att_prep_write_req_parse(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd)
+{
+    return ble_att_prep_write_cmd_parse(payload, len, cmd, 1);
+}
+
+int
+ble_att_prep_write_req_write(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd)
+{
+    return ble_att_prep_write_cmd_write(payload, len, cmd, 1);
+}
+
+int
+ble_att_prep_write_rsp_parse(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd)
+{
+    return ble_att_prep_write_cmd_parse(payload, len, cmd, 0);
+}
+
+int
+ble_att_prep_write_rsp_write(void *payload, int len,
+                             struct ble_att_prep_write_cmd *cmd)
+{
+    return ble_att_prep_write_cmd_write(payload, len, cmd, 0);
+}
+
+int
+ble_att_exec_write_req_parse(void *payload, int len,
+                             struct ble_att_exec_write_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_ATT_EXEC_WRITE_REQ_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    u8ptr = payload;
+
+    if (u8ptr[0] != BLE_ATT_OP_EXEC_WRITE_REQ) {
+        return BLE_HS_EINVAL;
+    }
+
+    req->baeq_flags = u8ptr[1];
+
+    return 0;
+}
+
+int
+ble_att_exec_write_req_write(void *payload, int len,
+                             struct ble_att_exec_write_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_ATT_EXEC_WRITE_REQ_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    u8ptr = payload;
+    u8ptr[0] = BLE_ATT_OP_EXEC_WRITE_REQ;
+    u8ptr[1] = req->baeq_flags;
+
+    return 0;
+}
+
+int
+ble_att_exec_write_rsp_write(void *payload, int len)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_ATT_EXEC_WRITE_RSP_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    u8ptr = payload;
+    u8ptr[0] = BLE_ATT_OP_EXEC_WRITE_REQ;
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_att_cmd.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_cmd.h b/net/nimble/host/src/ble_att_cmd.h
index bd48ab7..df2f160 100644
--- a/net/nimble/host/src/ble_att_cmd.h
+++ b/net/nimble/host/src/ble_att_cmd.h
@@ -185,12 +185,47 @@ struct ble_att_read_group_type_rsp {
  * | Attribute Handle                   | 2                 |
  * | Attribute Value                    | 0 to (ATT_MTU-3)  |
  */
-#define BLE_ATT_WRITE_REQ_BASE_SZ        3
+#define BLE_ATT_WRITE_REQ_BASE_SZ       3
 struct ble_att_write_req {
     uint16_t bawq_handle;
 };
 
-#define BLE_ATT_WRITE_RSP_SZ             1
+#define BLE_ATT_WRITE_RSP_SZ            1
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Attribute Handle                   | 2                 |
+ * | Value Offset                       | 2                 |
+ * | Part Attribute Value               | 0 to (ATT_MTU-5)  |
+ */
+#define BLE_ATT_PREP_WRITE_CMD_BASE_SZ  5
+struct ble_att_prep_write_cmd {
+    uint16_t bapc_handle;
+    uint16_t bapc_offset;
+};
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ * | Flags                              | 1                 |
+ */
+#define BLE_ATT_EXEC_WRITE_REQ_SZ       2
+struct ble_att_exec_write_req {
+    uint8_t baeq_flags;
+};
+
+#define BLE_ATT_EXEC_WRITE_F_CONFIRM    0x01
+
+/**
+ * | Parameter                          | Size (octets)     |
+ * +------------------------------------+-------------------+
+ * | Attribute Opcode                   | 1                 |
+ */
+#define BLE_ATT_EXEC_WRITE_RSP_SZ       1
+
 
 int ble_att_error_rsp_parse(void *payload, int len,
                             struct ble_att_error_rsp *rsp);
@@ -242,3 +277,16 @@ int ble_att_write_cmd_parse(void *payload, int len,
                             struct ble_att_write_req *req);
 int ble_att_write_cmd_write(void *payload, int len,
                             struct ble_att_write_req *req);
+int ble_att_prep_write_req_parse(void *payload, int len,
+                                 struct ble_att_prep_write_cmd *cmd);
+int ble_att_prep_write_req_write(void *payload, int len,
+                                 struct ble_att_prep_write_cmd *cmd);
+int ble_att_prep_write_rsp_parse(void *payload, int len,
+                                 struct ble_att_prep_write_cmd *cmd);
+int ble_att_prep_write_rsp_write(void *payload, int len,
+                                 struct ble_att_prep_write_cmd *cmd);
+int ble_att_exec_write_req_parse(void *payload, int len,
+                                 struct ble_att_exec_write_req *req);
+int ble_att_exec_write_req_write(void *payload, int len,
+                                 struct ble_att_exec_write_req *req);
+int ble_att_exec_write_rsp_write(void *payload, int len);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_att_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_att_priv.h b/net/nimble/host/src/ble_att_priv.h
index 92e9fd7..5b840f0 100644
--- a/net/nimble/host/src/ble_att_priv.h
+++ b/net/nimble/host/src/ble_att_priv.h
@@ -36,6 +36,19 @@ struct ble_att_write_req;
 #define BLE_ATT_MTU_DFLT         23  /* Also the minimum. */
 #define BLE_ATT_MTU_MAX          256 /* XXX: I'm making this up! */
 
+struct ble_att_prep_entry {
+    SLIST_ENTRY(ble_att_prep_entry) bape_next;
+    uint16_t bape_handle;
+    uint16_t bape_offset;
+    struct os_mbuf *bape_value;
+};
+
+struct ble_att_svr_conn {
+    /** This list is sorted by attribute handle ID. */
+    SLIST_HEAD(, ble_att_prep_entry) basc_prep_list;
+    uint32_t basc_prep_write_rx_time;
+};
+
 /**
  * Called from ble_att_svr_walk().  Called on each entry in the 
  * ble_att_svr_list.
@@ -87,6 +100,12 @@ int ble_att_svr_rx_read(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                         struct os_mbuf **rxom);
 int ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                          struct os_mbuf **rxom);
+int ble_att_svr_rx_prep_write(struct ble_hs_conn *conn,
+                              struct ble_l2cap_chan *chan,
+                              struct os_mbuf **rxom);
+int ble_att_svr_rx_exec_write(struct ble_hs_conn *conn,
+                              struct ble_l2cap_chan *chan,
+                              struct os_mbuf **rxom);
 int ble_att_svr_init(void);
 
 /*** @clt */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/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 d216bc3..4efaf5f 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -19,10 +19,11 @@
 #include <assert.h>
 #include "os/os.h"
 #include "nimble/ble.h"
+#include "host/ble_hs_uuid.h"
+#include "ble_hs_priv.h"
 #include "ble_hs_priv.h"
 #include "ble_l2cap.h"
 #include "ble_hs_conn.h"
-#include "host/ble_hs_uuid.h"
 #include "ble_att_cmd.h"
 #include "ble_att_priv.h"
 
@@ -35,6 +36,22 @@ static struct os_mutex ble_att_svr_list_mutex;
 static void *ble_att_svr_entry_mem;
 static struct os_mempool ble_att_svr_entry_pool;
 
+
+#define BLE_ATT_SVR_PREP_MBUF_BUF_SIZE         (128)
+#define BLE_ATT_SVR_PREP_MBUF_MEMBLOCK_SIZE                     \
+    (BLE_ATT_SVR_PREP_MBUF_BUF_SIZE + sizeof(struct os_mbuf) +  \
+     sizeof(struct os_mbuf_pkthdr))
+
+#define BLE_ATT_SVR_NUM_PREP_ENTRIES     8
+#define BLE_ATT_SVR_NUM_PREP_MBUFS       12
+static void *ble_att_svr_prep_entry_mem;
+static struct os_mempool ble_att_svr_prep_entry_pool;
+static void *ble_att_svr_prep_mbuf_mem;
+static struct os_mempool ble_att_svr_prep_mbuf_mempool;
+static struct os_mbuf_pool ble_att_svr_prep_mbuf_pool;
+
+static uint8_t ble_att_svr_flat_buf[BLE_ATT_ATTR_MAX_LEN];
+
 /**
  * Locks the host attribute list.
  *
@@ -1464,8 +1481,10 @@ ble_att_svr_rx_write(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
         goto send_err;
     }
 
-    arg.aha_write.om = *rxom;
+    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;
@@ -1484,43 +1503,423 @@ send_err:
     return rc;
 }
 
+static void
+ble_att_svr_prep_free(struct ble_att_prep_entry *entry)
+{
+    os_mbuf_free_chain(entry->bape_value);
+    os_memblock_put(&ble_att_svr_prep_entry_pool, entry);
+}
+
+static struct ble_att_prep_entry *
+ble_att_svr_prep_alloc(void)
+{
+    struct ble_att_prep_entry *entry;
+
+    entry = os_memblock_get(&ble_att_svr_prep_entry_pool);
+    if (entry == NULL) {
+        return NULL;
+    }
+
+    memset(entry, 0, sizeof *entry);
+    entry->bape_value = os_mbuf_get_pkthdr(&ble_att_svr_prep_mbuf_pool, 0);
+    if (entry->bape_value == NULL) {
+        ble_att_svr_prep_free(entry);
+        return NULL;
+    }
+
+    return entry;
+}
+
+static struct ble_att_prep_entry *
+ble_att_svr_prep_find_prev(struct ble_att_svr_conn *basc, uint16_t handle,
+                           uint16_t offset)
+{
+    struct ble_att_prep_entry *entry;
+    struct ble_att_prep_entry *prev;
+
+    prev = NULL;
+    SLIST_FOREACH(entry, &basc->basc_prep_list, bape_next) {
+        if (entry->bape_handle > handle) {
+            break;
+        }
+
+        if (entry->bape_handle == handle && entry->bape_offset > offset) {
+            break;
+        }
+
+        prev = entry;
+    }
+
+    return prev;
+}
+
+static void
+ble_att_svr_prep_clear(struct ble_att_svr_conn *basc)
+{
+    struct ble_att_prep_entry *entry;
+
+    while ((entry = SLIST_FIRST(&basc->basc_prep_list)) != NULL) {
+        SLIST_REMOVE_HEAD(&basc->basc_prep_list, bape_next);
+        ble_att_svr_prep_free(entry);
+    }
+}
+
+static int
+ble_att_svr_prep_validate(struct ble_att_svr_conn *basc, uint16_t *err_handle)
+{
+    struct ble_att_prep_entry *entry;
+    struct ble_att_prep_entry *prev;
+    int cur_len;
+
+    prev = NULL;
+    SLIST_FOREACH(entry, &basc->basc_prep_list, bape_next) {
+        if (prev == NULL || prev->bape_handle != entry->bape_handle) {
+            /* Ensure attribute write starts at offset 0. */
+            if (entry->bape_offset != 0) {
+                *err_handle = entry->bape_handle;
+                return BLE_ATT_ERR_INVALID_OFFSET;
+            }
+        } else {
+            /* Ensure entry continues where previous left off. */
+            if (prev->bape_offset + OS_MBUF_PKTLEN(prev->bape_value) !=
+                entry->bape_offset) {
+
+                *err_handle = entry->bape_handle;
+                return BLE_ATT_ERR_INVALID_OFFSET;
+            }
+        }
+
+        cur_len = entry->bape_offset + OS_MBUF_PKTLEN(prev->bape_value);
+        if (cur_len > BLE_ATT_ATTR_MAX_LEN) {
+            *err_handle = entry->bape_handle;
+            return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+        }
+
+        prev = entry;
+    }
+
+    return 0;
+}
+
+static int
+ble_att_svr_prep_write(struct ble_att_svr_conn *basc, uint16_t *err_handle)
+{
+    union ble_att_svr_handle_arg arg;
+    struct ble_att_prep_entry *entry;
+    struct ble_att_prep_entry *next;
+    struct ble_att_svr_entry *attr;
+    int buf_off;
+    int rc;
+
+    /* First, validate the contents of the prepare queue. */
+    rc = ble_att_svr_prep_validate(basc, err_handle);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Contents are valid; perform the writes. */
+    buf_off = 0;
+    entry = SLIST_FIRST(&basc->basc_prep_list);
+    while (entry != NULL) {
+        next = SLIST_NEXT(entry, bape_next);
+
+        rc = os_mbuf_copydata(entry->bape_value, 0,
+                              OS_MBUF_PKTLEN(entry->bape_value),
+                              ble_att_svr_flat_buf + buf_off);
+        assert(rc == 0);
+        buf_off += OS_MBUF_PKTLEN(entry->bape_value);
+
+        /* If this is the last entry for this attribute, perform the write. */
+        if (next == NULL || entry->bape_handle != next->bape_handle) {
+            attr = NULL;
+            rc = ble_att_svr_find_by_handle(entry->bape_handle, &attr);
+            if (rc != 0) {
+                *err_handle = entry->bape_handle;
+                return BLE_ATT_ERR_INVALID_HANDLE;
+            }
+
+            arg.aha_write.attr_data = ble_att_svr_flat_buf;
+            arg.aha_write.attr_len = buf_off;
+            rc = attr->ha_fn(attr, BLE_ATT_OP_WRITE_REQ, &arg);
+            if (rc != 0) {
+                *err_handle = entry->bape_handle;
+                return BLE_ATT_ERR_UNLIKELY;
+            }
+
+            buf_off = 0;
+        }
+
+        entry = next;
+    }
+
+    return 0;
+}
+
 int
-ble_att_svr_init(void)
+ble_att_svr_rx_prep_write(struct ble_hs_conn *conn,
+                          struct ble_l2cap_chan *chan,
+                          struct os_mbuf **rxom)
 {
+    struct ble_att_prep_write_cmd req;
+    struct ble_att_prep_entry *prep_entry;
+    struct ble_att_prep_entry *prep_prev;
+    struct ble_att_svr_entry *attr_entry;
+    struct os_mbuf *srcom;
+    struct os_mbuf *txom;
     int rc;
 
-    STAILQ_INIT(&ble_att_svr_list);
+    /* Initialize some values in case of early error. */
+    prep_entry = NULL;
+    req.bapc_handle = 0;
 
-    rc = os_mutex_init(&ble_att_svr_list_mutex);
+    *rxom = os_mbuf_pullup(*rxom, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
+    if (*rxom == NULL) {
+        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;
     }
 
-    free(ble_att_svr_entry_mem);
-    ble_att_svr_entry_mem = malloc(
-        OS_MEMPOOL_BYTES(BLE_ATT_SVR_NUM_ENTRIES,
-                         sizeof (struct ble_att_svr_entry)));
-    if (ble_att_svr_entry_mem == NULL) {
+    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;
+        goto err;
+    }
+
+    prep_entry = ble_att_svr_prep_alloc();
+    if (prep_entry == NULL) {
+        rc = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+        goto err;
+    }
+
+    prep_prev = ble_att_svr_prep_find_prev(&conn->bhc_att_svr, req.bapc_handle,
+                                           req.bapc_offset);
+    if (prep_prev == NULL) {
+        SLIST_INSERT_HEAD(&conn->bhc_att_svr.basc_prep_list, prep_entry,
+                          bape_next);
+    } else {
+        SLIST_INSERT_AFTER(prep_prev, prep_entry, bape_next);
+    }
+
+    /* Append attribute value from request onto prep mbuf. */
+    for (srcom = *rxom; srcom != NULL; srcom = SLIST_NEXT(srcom, om_next)) {
+        rc = os_mbuf_append(prep_entry->bape_value, srcom->om_data,
+                            srcom->om_len);
+        if (rc != 0) {
+            rc = BLE_ATT_ERR_PREPARE_QUEUE_FULL;
+            goto err;
+        }
+    }
+
+    /* The receive buffer now contains the attribute value.  Repurpose this
+     * buffer for the response.  Prepend a response header.
+     */
+    *rxom = os_mbuf_prepend(*rxom, BLE_ATT_PREP_WRITE_CMD_BASE_SZ);
+    if (*rxom == NULL) {
+        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;
+    }
+
+    rc = ble_l2cap_tx(conn, chan, txom);
+    if (rc != 0) {
+        rc = BLE_ATT_ERR_UNLIKELY;
+        goto err;
+    }
+
+    /* Make sure the receive buffer doesn't get freed since we are using it for
+     * the response.
+     */
+    *rxom = NULL;
+
+    return 0;
+
+err:
+    if (prep_entry != NULL) {
+        if (prep_prev == NULL) {
+            SLIST_REMOVE_HEAD(&conn->bhc_att_svr.basc_prep_list, bape_next);
+        } else {
+            SLIST_NEXT(prep_prev, bape_next) =
+                SLIST_NEXT(prep_entry, bape_next);
+        }
+
+        ble_att_svr_prep_free(prep_entry);
+    }
+
+    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_PREP_WRITE_REQ,
+                             req.bapc_handle, rc);
+    return rc;
+}
+
+static int
+ble_att_svr_tx_exec_write_rsp(struct ble_hs_conn *conn,
+                              struct ble_l2cap_chan *chan)
+{
+    struct os_mbuf *txom;
+    uint8_t *dst;
+    int rc;
+
+    txom = ble_att_get_pkthdr();
+    if (txom == NULL) {
+        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
+    dst = os_mbuf_extend(txom, BLE_ATT_EXEC_WRITE_RSP_SZ);
+    if (dst == NULL) {
+        rc = BLE_ATT_ERR_INSUFFICIENT_RES;
+        goto err;
+    }
+
+    rc = ble_att_exec_write_rsp_write(dst, BLE_ATT_EXEC_WRITE_RSP_SZ);
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_l2cap_tx(conn, chan, txom);
+    txom = NULL;
+    if (rc != 0) {
+        rc = BLE_ATT_ERR_UNLIKELY;
+        goto err;
+    }
+
+    return 0;
+
+err:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+int
+ble_att_svr_rx_exec_write(struct ble_hs_conn *conn,
+                          struct ble_l2cap_chan *chan,
+                          struct os_mbuf **rxom)
+{
+    struct ble_att_exec_write_req req;
+    uint16_t err_handle;
+    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) {
         rc = BLE_HS_ENOMEM;
         goto err;
     }
 
-    rc = os_mempool_init(&ble_att_svr_entry_pool,
-                         BLE_ATT_SVR_NUM_ENTRIES,
-                         sizeof (struct ble_att_svr_entry),
-                         ble_att_svr_entry_mem,
-                         "ble_att_svr_entry_pool");
+    rc = ble_att_exec_write_req_parse((*rxom)->om_data, (*rxom)->om_len, &req);
     if (rc != 0) {
         goto err;
     }
 
-    ble_att_svr_id = 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);
+    } else {
+        rc = 0;
+    }
+
+    /* Erase all prep entries. */
+    ble_att_svr_prep_clear(&conn->bhc_att_svr);
+
+    if (rc != 0) {
+        goto err;
+    }
+
+    /* Send response. */
+    rc = ble_att_svr_tx_exec_write_rsp(conn, chan);
+    if (rc != 0) {
+        goto err;
+    }
 
     return 0;
 
 err:
+    ble_att_svr_tx_error_rsp(conn, chan, BLE_ATT_OP_EXEC_WRITE_REQ,
+                             err_handle, rc);
+    return rc;
+}
+
+static void
+ble_att_svr_free_mem(void)
+{
     free(ble_att_svr_entry_mem);
     ble_att_svr_entry_mem = NULL;
 
+    free(ble_att_svr_prep_entry_mem);
+    ble_att_svr_prep_entry_mem = NULL;
+
+    free(ble_att_svr_prep_mbuf_mem);
+    ble_att_svr_prep_mbuf_mem = NULL;
+}
+
+int
+ble_att_svr_init(void)
+{
+    int rc;
+
+    ble_att_svr_free_mem();
+
+    STAILQ_INIT(&ble_att_svr_list);
+
+    rc = os_mutex_init(&ble_att_svr_list_mutex);
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_hs_misc_malloc_mempool(&ble_att_svr_entry_mem,
+                                    &ble_att_svr_entry_pool,
+                                    BLE_ATT_SVR_NUM_ENTRIES,
+                                    sizeof (struct ble_att_svr_entry),
+                                    "ble_att_svr_entry_pool");
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_hs_misc_malloc_mempool(&ble_att_svr_prep_entry_mem,
+                                    &ble_att_svr_prep_entry_pool,
+                                    BLE_ATT_SVR_NUM_PREP_ENTRIES,
+                                    sizeof (struct ble_att_prep_entry),
+                                    "ble_att_prep_entry_pool");
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = ble_hs_misc_malloc_mempool(&ble_att_svr_prep_mbuf_mem,
+                                    &ble_att_svr_prep_mbuf_mempool,
+                                    BLE_ATT_SVR_NUM_PREP_MBUFS,
+                                    BLE_ATT_SVR_PREP_MBUF_MEMBLOCK_SIZE,
+                                    "ble_att_prep_mbuf_mempool");
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = os_mbuf_pool_init(&ble_att_svr_prep_mbuf_pool,
+                           &ble_att_svr_prep_mbuf_mempool,
+                           BLE_ATT_SVR_PREP_MBUF_MEMBLOCK_SIZE,
+                           BLE_ATT_SVR_NUM_PREP_MBUFS);
+    if (rc != 0) {
+        rc = BLE_HS_EOS;
+        goto err;
+    }
+
+    ble_att_svr_id = 0;
+
+    return 0;
+
+err:
+    ble_att_svr_free_mem();
     return rc;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/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 0cae900..92a8e9f 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -79,7 +79,7 @@ struct ble_hs_pkt {
     struct tpq_elem bhp_tpq_elem;
     struct os_mbuf *bhp_om;
 };
-static os_membuf_t *ble_hs_pkt_mem;
+static void *ble_hs_pkt_mem;
 static struct os_mempool ble_hs_pkt_pool;
 
 static struct tpq ble_hs_rx_q;
@@ -264,28 +264,20 @@ ble_hs_init(uint8_t prio)
                          BLE_HS_MBUF_MEMBLOCK_SIZE,
                          ble_hs_mbuf_mem, "ble_hs_mbuf_pool");
     if (rc != 0) {
-        rc = BLE_HS_EINVAL; // XXX
+        rc = BLE_HS_EOS;
         goto err;
     }
     rc = os_mbuf_pool_init(&ble_hs_mbuf_pool, &ble_hs_mbuf_mempool,
                            BLE_HS_MBUF_MEMBLOCK_SIZE, BLE_HS_NUM_MBUFS);
     if (rc != 0) {
-        rc = BLE_HS_EINVAL; // XXX
+        rc = BLE_HS_EOS;
         goto err;
     }
 
-    ble_hs_pkt_mem = malloc(
-        OS_MEMPOOL_BYTES(BLE_HS_PKT_MAX,
-                         sizeof (struct ble_hs_pkt)));
-    if (ble_hs_pkt_mem == NULL) {
-        rc = BLE_HS_ENOMEM;
-        goto err;
-    }
-    rc = os_mempool_init(&ble_hs_pkt_pool, BLE_HS_PKT_MAX,
-                         sizeof (struct ble_hs_pkt),
-                         ble_hs_pkt_mem, "ble_hs_pkt_pool");
+    rc = ble_hs_misc_malloc_mempool(&ble_hs_pkt_mem, &ble_hs_pkt_pool,
+                                    BLE_HS_PKT_MAX, sizeof (struct ble_hs_pkt),
+                                    "ble_hs_pkt_pool");
     if (rc != 0) {
-        rc = BLE_HS_EINVAL; // XXX
         goto err;
     }
 
@@ -316,13 +308,6 @@ ble_hs_init(uint8_t prio)
         goto err;
     }
 
-#if 0
-    rc = ble_hs_hci_batch_init();
-    if (rc != 0) {
-        goto err;
-    }
-#endif
-
     rc = ble_gatt_init();
     if (rc != 0) {
         goto err;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_hs_conn.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.c b/net/nimble/host/src/ble_hs_conn.c
index b573bc7..43f401c 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -57,8 +57,6 @@ ble_hs_conn_alloc(void)
     }
     SLIST_INSERT_HEAD(&conn->bhc_channels, chan, blc_next);
 
-    SLIST_INIT(&conn->bhc_att_clt_list);
-
     return conn;
 
 err:

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_hs_conn.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_conn.h b/net/nimble/host/src/ble_hs_conn.h
index de75254..0951376 100644
--- a/net/nimble/host/src/ble_hs_conn.h
+++ b/net/nimble/host/src/ble_hs_conn.h
@@ -31,8 +31,7 @@ struct ble_hs_conn {
 
     struct ble_l2cap_chan_list bhc_channels;
 
-    /** Mapping of peer's ATT attributes to handle IDs. */
-    struct ble_att_clt_entry_list bhc_att_clt_list;
+    struct ble_att_svr_conn bhc_att_svr;
 };
 
 struct ble_hs_conn *ble_hs_conn_alloc(void);

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/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
new file mode 100644
index 0000000..9c076f5
--- /dev/null
+++ b/net/nimble/host/src/ble_hs_misc.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+#include "os/os.h"
+#include "ble_hs_priv.h"
+
+int
+ble_hs_misc_malloc_mempool(void **mem, struct os_mempool *pool,
+                           int num_entries, int entry_size, char *name)
+{
+    int rc;
+
+    *mem = malloc(OS_MEMPOOL_BYTES(num_entries, entry_size));
+    if (*mem == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    rc = os_mempool_init(pool, num_entries, entry_size, *mem, name);
+    if (rc != 0) {
+        free(*mem);
+        return BLE_HS_EOS;
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/ble_hs_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_hs_priv.h b/net/nimble/host/src/ble_hs_priv.h
index 6daedd6..00357dc 100644
--- a/net/nimble/host/src/ble_hs_priv.h
+++ b/net/nimble/host/src/ble_hs_priv.h
@@ -20,6 +20,7 @@
 #include <inttypes.h>
 #include "host/ble_hs.h"
 struct os_mbuf;
+struct os_mempool;
 
 #define BLE_HOST_HCI_EVENT_CTLR_EVENT   (OS_EVENT_T_PERUSER + 0)
 #define BLE_HS_KICK_HCI_EVENT           (OS_EVENT_T_PERUSER + 1)
@@ -36,4 +37,7 @@ int ble_hs_tx_data(struct os_mbuf *om);
 void ble_hs_kick_hci(void);
 void ble_hs_kick_gatt(void);
 
+int ble_hs_misc_malloc_mempool(void **mem, struct os_mempool *pool,
+                               int num_entries, int entry_size, char *name);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/762dccc3/net/nimble/host/src/test/ble_att_svr_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_att_svr_test.c b/net/nimble/host/src/test/ble_att_svr_test.c
index cfc1793..03e345a 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -214,23 +214,16 @@ static int
 ble_att_svr_test_misc_attr_fn_w_1(struct ble_att_svr_entry *entry, uint8_t op,
                                   union ble_att_svr_handle_arg *arg)
 {
-    struct os_mbuf_pkthdr *omp;
-    int rc;
-
     switch (op) {
     case BLE_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_att_svr_test_attr_w_1);
-        TEST_ASSERT(rc == 0);
+        memcpy(ble_att_svr_test_attr_w_1, arg->aha_write.attr_data,
+               arg->aha_write.attr_len);
         ble_att_svr_test_attr_w_1_len = arg->aha_write.attr_len;
         return 0;
 
     default:
         return -1;
     }
-
-    (void)omp;
 }
 
 static void