You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/01/30 00:56:19 UTC

[2/3] incubator-mynewt-larva git commit: L2CAP signal channel.

L2CAP signal channel.


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

Branch: refs/heads/master
Commit: 894a15c30ddb9a37275056c09d3ae4a3184d077f
Parents: ccda6d4
Author: Christopher Collins <cc...@gmail.com>
Authored: Thu Jan 28 14:00:31 2016 -0500
Committer: Christopher Collins <cc...@gmail.com>
Committed: Fri Jan 29 15:55:41 2016 -0800

----------------------------------------------------------------------
 net/nimble/host/include/host/ble_gap.h       |  15 +-
 net/nimble/host/include/host/ble_hs.h        |   4 +
 net/nimble/host/include/host/ble_l2cap.h     |  35 ++
 net/nimble/host/src/ble_att.c                |   2 +-
 net/nimble/host/src/ble_att_cmd.c            |   2 +-
 net/nimble/host/src/ble_att_svr.c            |   2 +-
 net/nimble/host/src/ble_gap_conn.c           |  59 +-
 net/nimble/host/src/ble_gap_priv.h           |   3 +
 net/nimble/host/src/ble_gattc.c              |  54 +-
 net/nimble/host/src/ble_hs.c                 |  22 +-
 net/nimble/host/src/ble_hs_conn.c            |   2 +-
 net/nimble/host/src/ble_hs_conn.h            |   2 +-
 net/nimble/host/src/ble_hs_misc.c            |  25 +
 net/nimble/host/src/ble_hs_priv.h            |   4 +
 net/nimble/host/src/ble_l2cap.c              |   7 +-
 net/nimble/host/src/ble_l2cap.h              | 114 ----
 net/nimble/host/src/ble_l2cap_priv.h         |  44 +-
 net/nimble/host/src/ble_l2cap_sig.c          | 720 +++++++++++++++++-----
 net/nimble/host/src/ble_l2cap_sig_cmd.c      | 277 +++++++++
 net/nimble/host/src/host_hci.c               |  19 +-
 net/nimble/host/src/host_hci_cmd.c           |   2 +-
 net/nimble/host/src/test/ble_att_svr_test.c  |   2 +-
 net/nimble/host/src/test/ble_host_hci_test.c |   2 +-
 net/nimble/host/src/test/ble_hs_adv_test.c   |   2 +-
 net/nimble/host/src/test/ble_hs_conn_test.c  |   2 +-
 net/nimble/host/src/test/ble_hs_test.c       |   2 +-
 net/nimble/host/src/test/ble_hs_test_util.c  |   3 +-
 net/nimble/host/src/test/ble_l2cap_test.c    | 561 +++++++++++++----
 project/bleshell/src/bleshell_priv.h         |   7 +-
 project/bleshell/src/cmd.c                   |  73 ++-
 project/bleshell/src/main.c                  |  18 +
 project/bleshell/src/parse.c                 |   6 +-
 32 files changed, 1634 insertions(+), 458 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/include/host/ble_gap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_gap.h b/net/nimble/host/include/host/ble_gap.h
index deeb1a4..3b8705e 100644
--- a/net/nimble/host/include/host/ble_gap.h
+++ b/net/nimble/host/include/host/ble_gap.h
@@ -87,13 +87,14 @@ struct hci_adv_params;
 #define BLE_GAP_EVENT_CONN                  0
 #define BLE_GAP_EVENT_CONN_UPDATED          1
 #define BLE_GAP_EVENT_CONN_UPDATE_REQ       2
-#define BLE_GAP_EVENT_CANCEL_FAILURE        3
-#define BLE_GAP_EVENT_TERM_FAILURE          4
-#define BLE_GAP_EVENT_DISC_SUCCESS          5
-#define BLE_GAP_EVENT_DISC_FINISHED         6
-#define BLE_GAP_EVENT_ADV_FINISHED          7
-#define BLE_GAP_EVENT_ADV_FAILURE           8
-#define BLE_GAP_EVENT_ADV_STOP_FAILURE      9
+#define BLE_GAP_EVENT_L2CAP_UPDATE_REQ      3
+#define BLE_GAP_EVENT_CANCEL_FAILURE        4
+#define BLE_GAP_EVENT_TERM_FAILURE          5
+#define BLE_GAP_EVENT_DISC_SUCCESS          6
+#define BLE_GAP_EVENT_DISC_FINISHED         7
+#define BLE_GAP_EVENT_ADV_FINISHED          8
+#define BLE_GAP_EVENT_ADV_FAILURE           9
+#define BLE_GAP_EVENT_ADV_STOP_FAILURE      10
 
 struct ble_gap_conn_desc {
     uint8_t peer_addr[6];

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 4e43f4c..eeff7d9 100644
--- a/net/nimble/host/include/host/ble_hs.h
+++ b/net/nimble/host/include/host/ble_hs.h
@@ -44,6 +44,7 @@
 #define BLE_HS_ETIMEOUT                 14
 #define BLE_HS_EDONE                    15
 #define BLE_HS_EBUSY                    16
+#define BLE_HS_EREJECT                  17
 
 #define BLE_HS_ERR_ATT_BASE             0x100   /* 256 */
 #define BLE_HS_ATT_ERR(x)               ((x) ? BLE_HS_ERR_ATT_BASE + (x) : 0)
@@ -51,6 +52,9 @@
 #define BLE_HS_ERR_HCI_BASE             0x200   /* 512 */
 #define BLE_HS_HCI_ERR(x)               ((x) ? BLE_HS_ERR_HCI_BASE + (x) : 0)
 
+#define BLE_HS_ERR_L2C_BASE             0x300
+#define BLE_HS_L2C_ERR(x)               ((x) ? BLE_HS_ERR_L2C_BASE + (x) : 0)
+
 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/894a15c3/net/nimble/host/include/host/ble_l2cap.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/include/host/ble_l2cap.h b/net/nimble/host/include/host/ble_l2cap.h
index 69b77c2..06b29ec 100644
--- a/net/nimble/host/include/host/ble_l2cap.h
+++ b/net/nimble/host/include/host/ble_l2cap.h
@@ -1,13 +1,48 @@
 #ifndef H_BLE_L2CAP_
 #define H_BLE_L2CAP_
 
+struct ble_l2cap_sig_update_req;
+struct ble_hs_conn;
+
 #define BLE_L2CAP_SIG_OP_REJECT             0x01
+#define BLE_L2CAP_SIG_OP_CONNECT_REQ        0x02
+#define BLE_L2CAP_SIG_OP_CONNECT_RSP        0x03
+#define BLE_L2CAP_SIG_OP_CONFIG_REQ         0x04
+#define BLE_L2CAP_SIG_OP_CONFIG_RSP         0x05
+#define BLE_L2CAP_SIG_OP_DISCONN_REQ        0x06
+#define BLE_L2CAP_SIG_OP_DISCONN_RSP        0x07
+#define BLE_L2CAP_SIG_OP_ECHO_REQ           0x08
+#define BLE_L2CAP_SIG_OP_ECHO_RSP           0x09
+#define BLE_L2CAP_SIG_OP_INFO_REQ           0x0a
+#define BLE_L2CAP_SIG_OP_INFO_RSP           0x0b
+#define BLE_L2CAP_SIG_OP_CREATE_CHAN_REQ    0x0c
+#define BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP    0x0d
+#define BLE_L2CAP_SIG_OP_MOVE_CHAN_REQ      0x0e
+#define BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP      0x0f
+#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_REQ 0x10
+#define BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP 0x11
 #define BLE_L2CAP_SIG_OP_UPDATE_REQ         0x12
 #define BLE_L2CAP_SIG_OP_UPDATE_RSP         0x13
+#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_REQ 0x14
+#define BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP 0x15
+#define BLE_L2CAP_SIG_OP_FLOW_CTRL_CREDIT   0x16
 #define BLE_L2CAP_SIG_OP_MAX                0x17
 
 #define BLE_L2CAP_ERR_CMD_NOT_UNDERSTOOD    0x0000
 #define BLE_L2CAP_ERR_MTU_EXCEEDED          0x0001
 #define BLE_L2CAP_ERR_INVALID_CID           0x0002
 
+typedef void ble_l2cap_sig_update_fn(int status, void *arg);
+
+struct ble_l2cap_sig_update_params {
+    uint16_t itvl_min;
+    uint16_t itvl_max;
+    uint16_t slave_latency;
+    uint16_t timeout_multiplier;
+};
+
+int ble_l2cap_sig_update(uint16_t conn_handle,
+                         struct ble_l2cap_sig_update_params *params,
+                         ble_l2cap_sig_update_fn *cb, void *cb_arg);
+
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 89e75b4..219a66c 100644
--- a/net/nimble/host/src/ble_att.c
+++ b/net/nimble/host/src/ble_att.c
@@ -17,7 +17,7 @@
 #include <stddef.h>
 #include <errno.h>
 #include "ble_hs_priv.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_cmd.h"
 #include "ble_att_priv.h"
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 6f358fb..61ff1ac 100644
--- a/net/nimble/host/src/ble_att_cmd.c
+++ b/net/nimble/host/src/ble_att_cmd.c
@@ -21,7 +21,7 @@
 #include "ble_hs_priv.h"
 #include "host/ble_att.h"
 #include "host/ble_uuid.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_cmd.h"
 
 int

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 e530848..9ff872f 100644
--- a/net/nimble/host/src/ble_att_svr.c
+++ b/net/nimble/host/src/ble_att_svr.c
@@ -23,7 +23,7 @@
 #include "host/ble_uuid.h"
 #include "ble_hs_priv.h"
 #include "ble_hs_priv.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_hs_conn.h"
 #include "ble_att_cmd.h"
 #include "ble_att_priv.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 7d51bf7..f51b81a 100644
--- a/net/nimble/host/src/ble_gap_conn.c
+++ b/net/nimble/host/src/ble_gap_conn.c
@@ -256,6 +256,18 @@ ble_gap_conn_update_entry_free(struct ble_gap_conn_update_entry *entry)
     assert(rc == 0);
 }
 
+static void
+ble_gap_conn_fill_desc(struct ble_hs_conn *conn,
+                       struct ble_gap_conn_desc *desc)
+{
+    desc->conn_handle = conn->bhc_handle;
+    desc->peer_addr_type = conn->bhc_addr_type;
+    memcpy(desc->peer_addr, conn->bhc_addr, sizeof desc->peer_addr);
+    desc->conn_itvl = conn->bhc_itvl;
+    desc->conn_latency = conn->bhc_latency;
+    desc->supervision_timeout = conn->bhc_supervision_timeout;
+}
+
 static int
 ble_gap_conn_call_conn_cb(int event, int status, struct ble_hs_conn *conn,
                           struct ble_gap_conn_upd_params *self_params,
@@ -269,14 +281,7 @@ ble_gap_conn_call_conn_cb(int event, int status, struct ble_hs_conn *conn,
     memset(&ctxt, 0, sizeof ctxt);
 
     if (conn != NULL) {
-        ctxt.desc.conn_handle = conn->bhc_handle;
-        ctxt.desc.peer_addr_type = conn->bhc_addr_type;
-        memcpy(ctxt.desc.peer_addr, conn->bhc_addr,
-               sizeof ctxt.desc.peer_addr);
-        ctxt.desc.conn_itvl = conn->bhc_itvl;
-        ctxt.desc.conn_latency = conn->bhc_latency;
-        ctxt.desc.supervision_timeout = conn->bhc_supervision_timeout;
-
+        ble_gap_conn_fill_desc(conn, &ctxt.desc);
         cb = conn->bhc_cb;
         cb_arg = conn->bhc_cb_arg;
     } else {
@@ -519,6 +524,19 @@ ble_gap_conn_update_failed(struct ble_gap_conn_update_entry *entry, int status)
     ble_gap_conn_update_entry_remove_free(entry);
 }
 
+static void
+ble_gap_conn_connection_broken(uint16_t conn_handle)
+{
+    struct ble_gap_conn_update_entry *entry;
+
+    entry = ble_gap_conn_update_find(conn_handle);
+    if (entry != NULL) {
+        ble_gap_conn_update_entry_remove_free(entry);
+    }
+
+    ble_gattc_connection_broken(conn_handle);
+}
+
 void
 ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
 {
@@ -532,14 +550,13 @@ ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt)
         }
         ble_gap_conn_call_conn_cb(BLE_GAP_EVENT_CONN, BLE_HS_ENOTCONN, conn,
                                   NULL, NULL);
+        ble_gap_conn_connection_broken(evt->connection_handle);
         ble_hs_conn_remove(conn);
         ble_hs_conn_free(conn);
     } else {
         ble_gap_conn_notify_master_term_failure(BLE_HS_HCI_ERR(evt->status),
                                                 evt->connection_handle);
     }
-
-    ble_gattc_connection_broken(evt->connection_handle);
 }
 
 void
@@ -787,6 +804,8 @@ ble_gap_conn_rx_conn_complete(struct hci_le_conn_complete *evt)
     if (conn != NULL) {
         /* XXX: Does this ever happen? */
         if (evt->status != 0) {
+            ble_gap_conn_connection_broken(evt->connection_handle);
+
             ble_hs_conn_remove(conn);
             ble_gap_conn_notify_connect(evt->status, conn);
             ble_hs_conn_free(conn);
@@ -2061,6 +2080,26 @@ ble_gap_conn_update_params(uint16_t conn_handle,
     return 0;
 }
 
+int
+ble_gap_conn_rx_l2cap_update_req(struct ble_hs_conn *conn,
+                                 struct ble_gap_conn_upd_params *params)
+{
+    struct ble_gap_conn_ctxt ctxt;
+    int rc;
+
+    if (conn->bhc_cb != NULL) {
+        ble_gap_conn_fill_desc(conn, &ctxt.desc);
+        ctxt.peer_params = params;
+        ctxt.self_params = NULL;
+        rc = conn->bhc_cb(BLE_GAP_EVENT_L2CAP_UPDATE_REQ, 0, &ctxt,
+                          conn->bhc_cb_arg);
+    } else {
+        rc = 0;
+    }
+
+    return rc;
+}
+
 /*****************************************************************************
  * $init                                                                     *
  *****************************************************************************/

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/ble_gap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap_priv.h b/net/nimble/host/src/ble_gap_priv.h
index 76e82e7..bcea4a1 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -34,10 +34,13 @@ int ble_gap_conn_rx_conn_complete(struct hci_le_conn_complete *evt);
 void ble_gap_conn_rx_disconn_complete(struct hci_disconn_complete *evt);
 void ble_gap_conn_rx_update_complete(struct hci_le_conn_upd_complete *evt);
 void ble_gap_conn_rx_param_req(struct hci_le_conn_param_req *evt);
+int ble_gap_conn_rx_l2cap_update_req(struct ble_hs_conn *conn,
+                                     struct ble_gap_conn_upd_params *params);
 int ble_gap_conn_master_in_progress(void);
 int ble_gap_conn_slave_in_progress(void);
 int ble_gap_conn_update_in_progress(uint16_t conn_handle);
 int ble_gap_conn_wl_busy(void);
+
 int ble_gap_conn_init(void);
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/ble_gattc.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gattc.c b/net/nimble/host/src/ble_gattc.c
index f79094f..4bfaef7 100644
--- a/net/nimble/host/src/ble_gattc.c
+++ b/net/nimble/host/src/ble_gattc.c
@@ -33,9 +33,9 @@
  * $definitions / declarations                                               *
  *****************************************************************************/
 
-#define BLE_GATT_NUM_ENTRIES                    16      /* XXX Configurable. */
+#define BLE_GATT_NUM_PROCS                      16      /* XXX Configurable. */
 #define BLE_GATT_HEARTBEAT_PERIOD               1000    /* Milliseconds. */
-#define BLE_GATT_UNRESPONSIVE_TIMEOUT           5000    /* Milliseconds. */
+#define BLE_GATT_UNRESPONSIVE_TIMEOUT           30000   /* Milliseconds. */
 
 #define BLE_GATT_OP_NONE                        UINT8_MAX
 #define BLE_GATT_OP_MTU                         0
@@ -175,16 +175,16 @@ struct ble_gattc_proc {
 };
 
 /** Procedure has a tx pending. */
-#define BLE_GATT_ENTRY_F_PENDING    0x01
+#define BLE_GATT_PROC_F_PENDING     0x01
 
 /** Procedure currently expects an ATT response. */
-#define BLE_GATT_ENTRY_F_EXPECTING  0x02
+#define BLE_GATT_PROC_F_EXPECTING   0x02
 
 /** Procedure failed to tx due to too many outstanding txes. */
-#define BLE_GATT_ENTRY_F_CONGESTED  0x04
+#define BLE_GATT_PROC_F_CONGESTED   0x04
 
 /** Procedure failed to tx due to memory exhaustion. */
-#define BLE_GATT_ENTRY_F_NO_MEM     0x08
+#define BLE_GATT_PROC_F_NO_MEM      0x08
 
 /**
  * Handles unresponsive timeouts and periodic retries in case of resource
@@ -640,7 +640,7 @@ ble_gattc_proc_matches(struct ble_gattc_proc *proc, uint16_t conn_handle,
     }
 
     if (expecting_only &&
-        !(proc->flags & BLE_GATT_ENTRY_F_EXPECTING)) {
+        !(proc->flags & BLE_GATT_PROC_F_EXPECTING)) {
 
         return 0;
     }
@@ -691,10 +691,10 @@ ble_gattc_proc_find(uint16_t conn_handle, uint8_t op, int expecting_only,
 static void
 ble_gattc_proc_set_pending(struct ble_gattc_proc *proc)
 {
-    assert(!(proc->flags & BLE_GATT_ENTRY_F_PENDING));
+    assert(!(proc->flags & BLE_GATT_PROC_F_PENDING));
 
-    proc->flags &= ~BLE_GATT_ENTRY_F_EXPECTING;
-    proc->flags |= BLE_GATT_ENTRY_F_PENDING;
+    proc->flags &= ~BLE_GATT_PROC_F_EXPECTING;
+    proc->flags |= BLE_GATT_PROC_F_PENDING;
     ble_hs_kick_gatt();
 }
 
@@ -706,11 +706,11 @@ static void
 ble_gattc_proc_set_expecting(struct ble_gattc_proc *proc,
                              struct ble_gattc_proc *prev)
 {
-    assert(!(proc->flags & BLE_GATT_ENTRY_F_EXPECTING));
+    assert(!(proc->flags & BLE_GATT_PROC_F_EXPECTING));
 
     ble_gattc_proc_remove(proc, prev);
-    proc->flags &= ~BLE_GATT_ENTRY_F_PENDING;
-    proc->flags |= BLE_GATT_ENTRY_F_EXPECTING;
+    proc->flags &= ~BLE_GATT_PROC_F_PENDING;
+    proc->flags |= BLE_GATT_PROC_F_EXPECTING;
     proc->tx_time = os_time_get();
     STAILQ_INSERT_TAIL(&ble_gattc_list, proc, next);
 }
@@ -763,9 +763,9 @@ ble_gattc_new_proc(uint16_t conn_handle, uint8_t op,
 static int
 ble_gattc_proc_can_pend(struct ble_gattc_proc *proc)
 {
-    return !(proc->flags & (BLE_GATT_ENTRY_F_CONGESTED |
-                            BLE_GATT_ENTRY_F_NO_MEM |
-                            BLE_GATT_ENTRY_F_EXPECTING));
+    return !(proc->flags & (BLE_GATT_PROC_F_CONGESTED |
+                            BLE_GATT_PROC_F_NO_MEM |
+                            BLE_GATT_PROC_F_EXPECTING));
 }
 
 /**
@@ -786,11 +786,11 @@ ble_gattc_tx_postpone_chk(struct ble_gattc_proc *proc, int rc)
 {
     switch (rc) {
     case BLE_HS_ECONGESTED:
-        proc->flags |= BLE_GATT_ENTRY_F_CONGESTED;
+        proc->flags |= BLE_GATT_PROC_F_CONGESTED;
         return 1;
 
     case BLE_HS_ENOMEM:
-        proc->flags |= BLE_GATT_ENTRY_F_NO_MEM;
+        proc->flags |= BLE_GATT_PROC_F_NO_MEM;
         return 1;
 
     default:
@@ -833,12 +833,12 @@ ble_gattc_heartbeat(void *unused)
     now = os_time_get();
 
     STAILQ_FOREACH(proc, &ble_gattc_list, next) {
-        if (proc->flags & BLE_GATT_ENTRY_F_NO_MEM) {
-            proc->flags &= ~BLE_GATT_ENTRY_F_NO_MEM;
+        if (proc->flags & BLE_GATT_PROC_F_NO_MEM) {
+            proc->flags &= ~BLE_GATT_PROC_F_NO_MEM;
             if (ble_gattc_proc_can_pend(proc)) {
                 ble_gattc_proc_set_pending(proc);
             }
-        } else if (proc->flags & BLE_GATT_ENTRY_F_EXPECTING) {
+        } else if (proc->flags & BLE_GATT_PROC_F_EXPECTING) {
             if (now - proc->tx_time >= BLE_GATT_UNRESPONSIVE_TIMEOUT) {
                 rc = ble_gap_conn_terminate(proc->conn_handle);
                 assert(rc == 0); /* XXX */
@@ -4070,7 +4070,7 @@ ble_gattc_wakeup(void)
     while (proc != NULL) {
         next = STAILQ_NEXT(proc, next);
 
-        if (proc->flags & BLE_GATT_ENTRY_F_PENDING) {
+        if (proc->flags & BLE_GATT_PROC_F_PENDING) {
             dispatch = ble_gattc_dispatch_get(proc->op);
             rc = dispatch->kick_cb(proc);
             switch (rc) {
@@ -4082,7 +4082,7 @@ ble_gattc_wakeup(void)
 
             case BLE_HS_EAGAIN:
                 /* Transmit failed due to resource shortage.  Reschedule. */
-                proc->flags &= ~BLE_GATT_ENTRY_F_PENDING;
+                proc->flags &= ~BLE_GATT_PROC_F_PENDING;
                 /* Current proc remains; reseat prev. */
                 prev = proc;
                 break;
@@ -4148,9 +4148,9 @@ ble_gattc_connection_txable(uint16_t conn_handle)
 
     STAILQ_FOREACH(proc, &ble_gattc_list, next) {
         if (proc->conn_handle == conn_handle &&
-            proc->flags & BLE_GATT_ENTRY_F_CONGESTED) {
+            proc->flags & BLE_GATT_PROC_F_CONGESTED) {
 
-            proc->flags &= ~BLE_GATT_ENTRY_F_CONGESTED;
+            proc->flags &= ~BLE_GATT_PROC_F_CONGESTED;
             if (ble_gattc_proc_can_pend(proc)) {
                 ble_gattc_proc_set_pending(proc);
             }
@@ -4189,7 +4189,7 @@ ble_gattc_init(void)
     free(ble_gattc_proc_mem);
 
     ble_gattc_proc_mem = malloc(
-        OS_MEMPOOL_BYTES(BLE_GATT_NUM_ENTRIES,
+        OS_MEMPOOL_BYTES(BLE_GATT_NUM_PROCS,
                          sizeof (struct ble_gattc_proc)));
     if (ble_gattc_proc_mem == NULL) {
         rc = BLE_HS_ENOMEM;
@@ -4197,7 +4197,7 @@ ble_gattc_init(void)
     }
 
     rc = os_mempool_init(&ble_gattc_proc_pool,
-                         BLE_GATT_NUM_ENTRIES,
+                         BLE_GATT_NUM_PROCS,
                          sizeof (struct ble_gattc_proc),
                          ble_gattc_proc_mem,
                          "ble_gattc_proc_pool");

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 037b8a0..2c09518 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -74,6 +74,7 @@ struct os_mbuf_pool ble_hs_mbuf_pool;
 struct os_eventq ble_hs_evq;
 static struct os_event ble_hs_kick_hci_ev;
 static struct os_event ble_hs_kick_gatt_ev;
+static struct os_event ble_hs_kick_l2cap_sig_ev;
 
 static struct os_mqueue ble_hs_rx_q;
 static struct os_mqueue ble_hs_tx_q;
@@ -141,6 +142,10 @@ ble_hs_task_handler(void *arg)
             ble_gattc_wakeup();
             break;
 
+        case BLE_HS_KICK_L2CAP_SIG_EVENT:
+            ble_l2cap_sig_wakeup();
+            break;
+
         default:
             assert(0);
             break;
@@ -184,7 +189,7 @@ ble_hs_tx_data(struct os_mbuf *om)
 }
 
 /**
- * Wakes the BLE host task so that it can process hci_batch events.
+ * Wakes the BLE host task so that it can process hci events.
  */
 void
 ble_hs_kick_hci(void)
@@ -193,7 +198,7 @@ ble_hs_kick_hci(void)
 }
 
 /**
- * Wakes the BLE host task so that it can process att_batch events.
+ * Wakes the BLE host task so that it can process GATT events.
  */
 void
 ble_hs_kick_gatt(void)
@@ -202,6 +207,15 @@ ble_hs_kick_gatt(void)
 }
 
 /**
+ * Wakes the BLE host task so that it can process L2CAP sig events.
+ */
+void
+ble_hs_kick_l2cap_sig(void)
+{
+    os_eventq_put(&ble_hs_evq, &ble_hs_kick_l2cap_sig_ev);
+}
+
+/**
  * Initializes the host portion of the BLE stack.
  */
 int
@@ -290,6 +304,10 @@ ble_hs_init(uint8_t prio)
     ble_hs_kick_gatt_ev.ev_type = BLE_HS_KICK_GATT_EVENT;
     ble_hs_kick_gatt_ev.ev_arg = NULL;
 
+    ble_hs_kick_l2cap_sig_ev.ev_queued = 0;
+    ble_hs_kick_l2cap_sig_ev.ev_type = BLE_HS_KICK_L2CAP_SIG_EVENT;
+    ble_hs_kick_l2cap_sig_ev.ev_arg = NULL;
+
     os_mqueue_init(&ble_hs_rx_q, NULL);
     os_mqueue_init(&ble_hs_tx_q, NULL);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 26e7d6c..1ed3849 100644
--- a/net/nimble/host/src/ble_hs_conn.c
+++ b/net/nimble/host/src/ble_hs_conn.c
@@ -20,7 +20,7 @@
 #include "os/os.h"
 #include "host/host_hci.h"
 #include "ble_hs_priv.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_l2cap_sig.h"
 #include "ble_att_priv.h"
 #include "ble_gatt_priv.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 2e6c62d..bdea85c 100644
--- a/net/nimble/host/src/ble_hs_conn.h
+++ b/net/nimble/host/src/ble_hs_conn.h
@@ -19,7 +19,7 @@
 
 #include "os/queue.h"
 #include "host/ble_gap.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_priv.h"
 #include "ble_gatt_priv.h"
 struct hci_le_conn_complete;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 42f4596..085c743 100644
--- a/net/nimble/host/src/ble_hs_misc.c
+++ b/net/nimble/host/src/ble_hs_misc.c
@@ -16,6 +16,7 @@
 
 #include <stdlib.h>
 #include "os/os.h"
+#include "console/console.h"
 #include "ble_hs_priv.h"
 
 int
@@ -37,3 +38,27 @@ ble_hs_misc_malloc_mempool(void **mem, struct os_mempool *pool,
 
     return 0;
 }
+
+void
+ble_hs_misc_log_mbuf(struct os_mbuf *om)
+{
+    uint8_t u8;
+    int i;
+
+    for (i = 0; i < OS_MBUF_PKTLEN(om); i++) {
+        os_mbuf_copydata(om, i, 1, &u8);
+        console_printf("0x%02x ", u8);
+    }
+}
+
+void
+ble_hs_misc_log_flat_buf(void *data, int len)
+{
+    uint8_t *u8ptr;
+    int i;
+
+    u8ptr = data;
+    for (i = 0; i < len; i++) {
+        console_printf("0x%02x ", u8ptr[i]);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 80b5997..119dc32 100644
--- a/net/nimble/host/src/ble_hs_priv.h
+++ b/net/nimble/host/src/ble_hs_priv.h
@@ -25,6 +25,7 @@ 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)
 #define BLE_HS_KICK_GATT_EVENT          (OS_EVENT_T_PERUSER + 2)
+#define BLE_HS_KICK_L2CAP_SIG_EVENT     (OS_EVENT_T_PERUSER + 3)
 
 extern struct os_mbuf_pool ble_hs_mbuf_pool;
 extern struct os_eventq ble_hs_evq;
@@ -34,9 +35,12 @@ int ble_hs_rx_data(struct os_mbuf *om);
 int ble_hs_tx_data(struct os_mbuf *om);
 void ble_hs_kick_hci(void);
 void ble_hs_kick_gatt(void);
+void ble_hs_kick_l2cap_sig(void);
 
 int ble_hs_misc_malloc_mempool(void **mem, struct os_mempool *pool,
                                int num_entries, int entry_size, char *name);
+void ble_hs_misc_log_mbuf(struct os_mbuf *om);
+void ble_hs_misc_log_flat_buf(void *data, int len);
 
 void ble_hs_cfg_init(void);
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 1b0a819..008f5bc 100644
--- a/net/nimble/host/src/ble_l2cap.c
+++ b/net/nimble/host/src/ble_l2cap.c
@@ -23,7 +23,7 @@
 #include "ble_hs_priv.h"
 #include "host/host_hci.h"
 #include "ble_hs_conn.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 
 _Static_assert(sizeof (struct ble_l2cap_hdr) == BLE_L2CAP_HDR_SZ,
                "struct ble_l2cap_hdr must be 4 bytes");
@@ -288,6 +288,11 @@ ble_l2cap_init(void)
         goto err;
     }
 
+    rc = ble_l2cap_sig_init();
+    if (rc != 0) {
+        goto err;
+    }
+
     return 0;
 
 err:

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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
deleted file mode 100644
index d32419b..0000000
--- a/net/nimble/host/src/ble_l2cap.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * 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.
- */
-
-#ifndef H_L2CAP_PRIV_
-#define H_L2CAP_PRIV_
-
-#include "host/ble_l2cap.h"
-#include <inttypes.h>
-#include "os/queue.h"
-#include "os/os_mbuf.h"
-struct ble_hs_conn;
-struct hci_data_hdr;
-
-#define BLE_L2CAP_SIG_HDR_SZ                4
-struct ble_l2cap_sig_hdr {
-    uint8_t op;
-    uint8_t identifier;
-    uint16_t length;
-};
-
-#define BLE_L2CAP_SIG_REJECT_MIN_SZ         2
-struct ble_l2cap_sig_reject {
-    uint16_t reason;
-};
-
-#define BLE_L2CAP_SIG_UPDATE_REQ_SZ         8
-struct ble_l2cap_sig_update_req {
-    uint16_t itvl_min;
-    uint16_t itvl_max;
-    uint16_t slave_latency;
-    uint16_t timeout_multiplier;
-};
-
-#define BLE_L2CAP_SIG_UPDATE_RSP_SZ         2
-struct ble_l2cap_sig_update_rsp {
-    uint16_t result;
-};
-
-
-#define BLE_L2CAP_CID_ATT   4
-#define BLE_L2CAP_CID_SIG   5
-
-#define BLE_L2CAP_HDR_SZ    4
-
-struct ble_l2cap_hdr
-{
-    uint16_t blh_len;
-    uint16_t blh_cid;
-};
-
-struct ble_l2cap_chan;
-
-typedef int ble_l2cap_rx_fn(struct ble_hs_conn *conn,
-                            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);
-
-typedef uint8_t ble_l2cap_chan_flags;
-#define BLE_L2CAP_CHAN_F_TXED_MTU       0x01    /* We have sent our MTU. */
-
-struct ble_l2cap_chan
-{
-    SLIST_ENTRY(ble_l2cap_chan) blc_next;
-    uint16_t blc_cid;
-    uint16_t blc_my_mtu;
-    uint16_t blc_peer_mtu;      /* 0 if not exchanged. */
-    uint16_t blc_default_mtu;
-    ble_l2cap_chan_flags blc_flags;
-
-    struct os_mbuf *blc_rx_buf;
-    uint16_t blc_rx_len;        /* Length of current reassembled rx packet. */
-
-    ble_l2cap_rx_fn *blc_rx_fn;
-};
-
-
-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);
-
-uint16_t ble_l2cap_chan_mtu(struct ble_l2cap_chan *chan);
-
-int ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
-                        struct ble_l2cap_hdr *l2cap_hdr);
-struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid,
-                                      uint16_t len);
-
-int ble_l2cap_rx(struct ble_hs_conn *connection,
-                 struct hci_data_hdr *hci_hdr,
-                 struct os_mbuf *om);
-int ble_l2cap_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
-                 struct os_mbuf *om);
-
-int ble_l2cap_init(void);
-
-extern struct os_mbuf_pool ble_l2cap_mbuf_pool;
-
-#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/ble_l2cap_priv.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_priv.h b/net/nimble/host/src/ble_l2cap_priv.h
index d32419b..4f8940b 100644
--- a/net/nimble/host/src/ble_l2cap_priv.h
+++ b/net/nimble/host/src/ble_l2cap_priv.h
@@ -49,6 +49,9 @@ struct ble_l2cap_sig_update_rsp {
     uint16_t result;
 };
 
+#define BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT  0x0000
+#define BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT  0x0001
+
 
 #define BLE_L2CAP_CID_ATT   4
 #define BLE_L2CAP_CID_SIG   5
@@ -91,15 +94,44 @@ struct ble_l2cap_chan
 
 SLIST_HEAD(ble_l2cap_chan_list, ble_l2cap_chan);
 
+int ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
+                        struct ble_l2cap_hdr *l2cap_hdr);
+struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid,
+                                      uint16_t len);
+
+int ble_l2cap_sig_hdr_parse(void *payload, uint16_t len,
+                            struct ble_l2cap_sig_hdr *hdr);
+int ble_l2cap_sig_hdr_write(void *payload, uint16_t len,
+                            struct ble_l2cap_sig_hdr *hdr);
+int ble_l2cap_sig_reject_write(void *payload, uint16_t len,
+                               struct ble_l2cap_sig_hdr *hdr,
+                               struct ble_l2cap_sig_reject *cmd);
+int ble_l2cap_sig_reject_tx(struct ble_hs_conn *conn,
+                            struct ble_l2cap_chan *chan,
+                            uint8_t id, uint16_t reason);
+int ble_l2cap_sig_update_req_parse(void *payload, int len,
+                                   struct ble_l2cap_sig_update_req *req);
+int ble_l2cap_sig_update_req_write(void *payload, int len,
+                                   struct ble_l2cap_sig_hdr *hdr,
+                                   struct ble_l2cap_sig_update_req *req);
+int ble_l2cap_sig_update_req_tx(struct ble_hs_conn *conn,
+                                struct ble_l2cap_chan *chan, uint8_t id,
+                                struct ble_l2cap_sig_update_req *req);
+int ble_l2cap_sig_update_rsp_parse(void *payload, int len,
+                                   struct ble_l2cap_sig_update_rsp *cmd);
+int ble_l2cap_sig_update_rsp_write(void *payload, int len,
+                                   struct ble_l2cap_sig_hdr *hdr,
+                                   struct ble_l2cap_sig_update_rsp *cmd);
+int ble_l2cap_sig_update_rsp_tx(struct ble_hs_conn *conn,
+                                struct ble_l2cap_chan *chan, uint8_t id,
+                                uint16_t result);
+
+
 struct ble_l2cap_chan *ble_l2cap_chan_alloc(void);
 void ble_l2cap_chan_free(struct ble_l2cap_chan *chan);
 
 uint16_t ble_l2cap_chan_mtu(struct ble_l2cap_chan *chan);
 
-int ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
-                        struct ble_l2cap_hdr *l2cap_hdr);
-struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid,
-                                      uint16_t len);
 
 int ble_l2cap_rx(struct ble_hs_conn *connection,
                  struct hci_data_hdr *hci_hdr,
@@ -107,8 +139,8 @@ int ble_l2cap_rx(struct ble_hs_conn *connection,
 int ble_l2cap_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
                  struct os_mbuf *om);
 
+void ble_l2cap_sig_wakeup(void);
+int ble_l2cap_sig_init(void);
 int ble_l2cap_init(void);
 
-extern struct os_mbuf_pool ble_l2cap_mbuf_pool;
-
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/ble_l2cap_sig.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sig.c b/net/nimble/host/src/ble_l2cap_sig.c
index cdfa9ff..43d0125 100644
--- a/net/nimble/host/src/ble_l2cap_sig.c
+++ b/net/nimble/host/src/ble_l2cap_sig.c
@@ -21,25 +21,149 @@
 #include "nimble/ble.h"
 #include "ble_hs_priv.h"
 #include "ble_hs_conn.h"
+#include "ble_gap_priv.h"
 #include "ble_l2cap_priv.h"
 #include "ble_att_priv.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_l2cap_sig.h"
 
+/*****************************************************************************
+ * $definitions / declarations                                               *
+ *****************************************************************************/
+
+#define BLE_L2CAP_SIG_NUM_PROCS                 16      /* XXX Configurable. */
+#define BLE_L2CAP_SIG_HEARTBEAT_PERIOD          1000    /* Milliseconds. */
+#define BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT      30000   /* Milliseconds. */
+
+#define BLE_L2CAP_SIG_PROC_OP_UPDATE            0
+#define BLE_L2CAP_SIG_PROC_OP_MAX               1
+
+struct ble_l2cap_sig_proc {
+    STAILQ_ENTRY(ble_l2cap_sig_proc) next;
+
+    uint8_t op;
+    uint8_t id;
+    uint8_t flags;
+    uint16_t conn_handle;
+    uint32_t tx_time; /* OS ticks. */
+    union {
+        struct {
+            struct ble_l2cap_sig_update_params params;
+            ble_l2cap_sig_update_fn *cb;
+            void *cb_arg;
+        } update;
+    };
+};
+
+/** Procedure has a tx pending. */
+#define BLE_L2CAP_SIG_PROC_F_PENDING    0x01
+
+/** Procedure currently expects an ATT response. */
+#define BLE_L2CAP_SIG_PROC_F_EXPECTING  0x02
+
+/** Procedure failed to tx due to too many outstanding txes. */
+#define BLE_L2CAP_SIG_PROC_F_CONGESTED  0x04
+
+/** Procedure failed to tx due to memory exhaustion. */
+#define BLE_L2CAP_SIG_PROC_F_NO_MEM     0x08
+
+/**
+ * Handles unresponsive timeouts and periodic retries in case of resource
+ * shortage.
+ */
+static struct os_callout_func ble_l2cap_sig_heartbeat_timer;
+
+typedef int ble_l2cap_sig_kick_fn(struct ble_l2cap_sig_proc *proc);
+
 typedef int ble_l2cap_sig_rx_fn(struct ble_hs_conn *conn,
                                 struct ble_l2cap_chan *chan,
                                 struct ble_l2cap_sig_hdr *hdr,
                                 struct os_mbuf **om);
 
+static int ble_l2cap_sig_rx_noop(struct ble_hs_conn *conn,
+                                 struct ble_l2cap_chan *chan,
+                                 struct ble_l2cap_sig_hdr *hdr,
+                                 struct os_mbuf **om);
 static int ble_l2cap_sig_update_req_rx(struct ble_hs_conn *conn,
                                        struct ble_l2cap_chan *chan,
                                        struct ble_l2cap_sig_hdr *hdr,
                                        struct os_mbuf **om);
+static int ble_l2cap_sig_update_rsp_rx(struct ble_hs_conn *conn,
+                                       struct ble_l2cap_chan *chan,
+                                       struct ble_l2cap_sig_hdr *hdr,
+                                       struct os_mbuf **om);
 
-static ble_l2cap_sig_rx_fn *ble_l2cap_sig_dispatch[] = {
-    [BLE_L2CAP_SIG_OP_UPDATE_REQ] = ble_l2cap_sig_update_req_rx,
+static int ble_l2cap_sig_update_kick(struct ble_l2cap_sig_proc *proc);
+
+static ble_l2cap_sig_rx_fn * const ble_l2cap_sig_dispatch[] = {
+    [BLE_L2CAP_SIG_OP_REJECT]               = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_CONNECT_RSP]          = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_CONFIG_RSP]           = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_DISCONN_RSP]          = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_ECHO_RSP]             = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_INFO_RSP]             = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_CREATE_CHAN_RSP]      = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_MOVE_CHAN_RSP]        = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_MOVE_CHAN_CONF_RSP]   = ble_l2cap_sig_rx_noop,
+    [BLE_L2CAP_SIG_OP_UPDATE_REQ]           = ble_l2cap_sig_update_req_rx,
+    [BLE_L2CAP_SIG_OP_UPDATE_RSP]           = ble_l2cap_sig_update_rsp_rx,
+    [BLE_L2CAP_SIG_OP_CREDIT_CONNECT_RSP]   = ble_l2cap_sig_rx_noop,
 };
 
+static ble_l2cap_sig_kick_fn * const ble_l2cap_sig_kick[] = {
+    [BLE_L2CAP_SIG_PROC_OP_UPDATE]          = ble_l2cap_sig_update_kick,
+};
+
+static uint8_t ble_l2cap_sig_cur_id;
+
+static void *ble_l2cap_sig_proc_mem;
+static struct os_mempool ble_l2cap_sig_proc_pool;
+
+static STAILQ_HEAD(, ble_l2cap_sig_proc) ble_l2cap_sig_list;
+
+/*****************************************************************************
+ * $debug                                                                    *
+ *****************************************************************************/
+
+/**
+ * Ensures all procedure entries are in a valid state.
+ */
+static void
+ble_l2cap_sig_assert_sanity(void)
+{
+#ifdef BLE_HS_DEBUG
+    struct ble_l2cap_sig_proc *proc;
+    unsigned mask;
+    int num_set;
+
+    STAILQ_FOREACH(proc, &ble_l2cap_sig_list, next) {
+        /* Ensure exactly one flag is set. */
+        num_set = 0;
+        mask = 0x01;
+        while (mask <= UINT8_MAX) {
+            if (proc->flags & mask) {
+                num_set++;
+            }
+            mask <<= 1;
+        }
+
+        assert(num_set == 1);
+    }
+#endif
+}
+
+static uint8_t
+ble_l2cap_sig_next_id(void)
+{
+    ble_l2cap_sig_cur_id++;
+    if (ble_l2cap_sig_cur_id == 0) {
+        /* An ID of 0 is illegal. */
+        ble_l2cap_sig_cur_id = 1;
+    }
+
+    return ble_l2cap_sig_cur_id;
+}
+
 static ble_l2cap_sig_rx_fn *
 ble_l2cap_sig_dispatch_get(uint8_t op)
 {
@@ -50,191 +174,248 @@ ble_l2cap_sig_dispatch_get(uint8_t op)
     return ble_l2cap_sig_dispatch[op];
 }
 
-static int
-ble_l2cap_sig_hdr_parse(void *payload, uint16_t len,
-                        struct ble_l2cap_sig_hdr *hdr)
+static ble_l2cap_sig_kick_fn *
+ble_l2cap_sig_kick_get(uint8_t op)
 {
-    uint8_t *u8ptr;
-
-    if (len < BLE_L2CAP_SIG_HDR_SZ) {
-        return BLE_HS_EBADDATA;
+    if (op > BLE_L2CAP_SIG_PROC_OP_MAX) {
+        return NULL;
     }
 
-    u8ptr = payload;
-    hdr->op = u8ptr[0];
-    hdr->identifier = u8ptr[1];
-    hdr->length = le16toh(u8ptr + 2);
-
-    return 0;
+    return ble_l2cap_sig_kick[op];
 }
 
+/**
+ * Determines if the specified proc entry's "pending" flag can be set.
+ */
 static int
-ble_l2cap_sig_hdr_write(void *payload, uint16_t len,
-                        struct ble_l2cap_sig_hdr *hdr)
+ble_l2cap_sig_proc_can_pend(struct ble_l2cap_sig_proc *proc)
 {
-    uint8_t *u8ptr;
+    return !(proc->flags & (BLE_L2CAP_SIG_PROC_F_CONGESTED |
+                            BLE_L2CAP_SIG_PROC_F_NO_MEM |
+                            BLE_L2CAP_SIG_PROC_F_EXPECTING));
+}
 
-    if (len < BLE_L2CAP_SIG_HDR_SZ) {
-        return BLE_HS_EBADDATA;
-    }
+/**
+ * Allocates a proc entry.
+ *
+ * @return                      An entry on success; null on failure.
+ */
+static struct ble_l2cap_sig_proc *
+ble_l2cap_sig_proc_alloc(void)
+{
+    struct ble_l2cap_sig_proc *proc;
 
-    u8ptr = payload;
-    u8ptr[0] = hdr->op;
-    u8ptr[1] = hdr->identifier;
-    htole16(u8ptr + 2, hdr->length);
+    proc = os_memblock_get(&ble_l2cap_sig_proc_pool);
+    if (proc != NULL) {
+        memset(proc, 0, sizeof *proc);
+    }
 
-    return 0;
+    return proc;
 }
 
-static int
-ble_l2cap_sig_reject_write(void *payload, uint16_t len,
-                           struct ble_l2cap_sig_hdr *hdr,
-                           struct ble_l2cap_sig_reject *cmd)
+/**
+ * Frees the specified proc entry.  No-op if passed a null pointer.
+ */
+static void
+ble_l2cap_sig_proc_free(struct ble_l2cap_sig_proc *proc)
 {
-    uint8_t *u8ptr;
     int rc;
 
-    u8ptr = payload;
-    rc = ble_l2cap_sig_hdr_write(u8ptr, len, hdr);
-    if (rc != 0) {
-        return rc;
+    if (proc != NULL) {
+        rc = os_memblock_put(&ble_l2cap_sig_proc_pool, proc);
+        assert(rc == 0);
     }
+}
 
-    u8ptr += BLE_L2CAP_SIG_HDR_SZ;
-    len -= BLE_L2CAP_SIG_HDR_SZ;
-
-    if (len < BLE_L2CAP_SIG_REJECT_MIN_SZ) {
-        return BLE_HS_EMSGSIZE;
+/**
+ * Removes the specified proc entry from the global list without freeing it.
+ *
+ * @param proc                  The proc to remove.
+ * @param prev                  The proc that is previous to "proc" in the
+ *                                  list; null if "proc" is the list head.
+ */
+static void
+ble_l2cap_sig_proc_remove(struct ble_l2cap_sig_proc *proc,
+                          struct ble_l2cap_sig_proc *prev)
+{
+    if (prev == NULL) {
+        assert(STAILQ_FIRST(&ble_l2cap_sig_list) == proc);
+        STAILQ_REMOVE_HEAD(&ble_l2cap_sig_list, next);
+    } else {
+        assert(STAILQ_NEXT(prev, next) == proc);
+        STAILQ_NEXT(prev, next) = STAILQ_NEXT(proc, next);
     }
+}
 
-    htole16(u8ptr, cmd->reason);
-
-    return 0;
+/**
+ * Removes and frees the speicifed proc entry.
+ *
+ * @param proc                  The proc to remove and free.
+ * @param prev                  The proc that is previous to "proc" in the
+ *                                  list; null if "proc" is the list head.
+ */
+static void
+ble_l2cap_sig_proc_remove_free(struct ble_l2cap_sig_proc *proc,
+                               struct ble_l2cap_sig_proc *prev)
+{
+    ble_l2cap_sig_proc_remove(proc, prev);
+    ble_l2cap_sig_proc_free(proc);
 }
 
 static int
-ble_l2cap_sig_reject_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
-                        uint8_t identifier, uint16_t reason)
+ble_l2cap_sig_new_proc(uint16_t conn_handle, uint8_t op,
+                       struct ble_l2cap_sig_proc **out_proc)
 {
-    /* XXX: Add support for optional data field. */
+    struct ble_hs_conn *conn;
 
-    struct ble_l2cap_sig_reject cmd;
-    struct ble_l2cap_sig_hdr hdr;
-    struct os_mbuf *txom;
-    void *v;
-    int rc;
+    *out_proc = NULL;
 
-    txom = ble_att_get_pkthdr();
-    if (txom == NULL) {
-        return BLE_HS_ENOMEM;
+    /* Ensure we have a connection with the specified handle. */
+    conn = ble_hs_conn_find(conn_handle);
+    if (conn == NULL) {
+        return BLE_HS_ENOTCONN;
     }
 
-    v = os_mbuf_extend(txom,
-                       BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_REJECT_MIN_SZ);
-    if (v == NULL) {
+    *out_proc = ble_l2cap_sig_proc_alloc();
+    if (*out_proc == NULL) {
         return BLE_HS_ENOMEM;
     }
 
-    hdr.op = BLE_L2CAP_SIG_OP_REJECT;
-    hdr.identifier = identifier;
-    hdr.length = BLE_L2CAP_SIG_REJECT_MIN_SZ;
-    cmd.reason = reason;
+    memset(*out_proc, 0, sizeof **out_proc);
+    (*out_proc)->op = op;
+    (*out_proc)->conn_handle = conn_handle;
 
-    rc = ble_l2cap_sig_reject_write(
-        v, BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_REJECT_MIN_SZ, &hdr, &cmd);
-    if (rc != 0) {
-        return rc;
-    }
+    STAILQ_INSERT_TAIL(&ble_l2cap_sig_list, *out_proc, next);
 
-    rc = ble_l2cap_tx(conn, chan, txom);
-    return rc;
+    return 0;
 }
 
+/**
+ * Tests if a proc entry fits the specified criteria.
+ *
+ * @param proc                  The procedure to test.
+ * @param conn_handle           The connection handle to match against.
+ * @param op                    The op code to match against/
+ * @param id                    The identifier to match against.
+ * @param expecting_only        1=Only match entries expecting a response;
+ *                                  0=Ignore this criterion.
+ *
+ * @return                      1 if the proc matches; 0 otherwise.
+ */
 static int
-ble_l2cap_sig_update_req_parse(void *payload, int len,
-                               struct ble_l2cap_sig_update_req *req)
+ble_l2cap_sig_proc_matches(struct ble_l2cap_sig_proc *proc,
+                           uint16_t conn_handle, uint8_t op, uint8_t id,
+                           int expecting_only)
 {
-    uint8_t *u8ptr;
+    if (conn_handle != proc->conn_handle) {
+        return 0;
+    }
 
-    if (len < BLE_L2CAP_SIG_UPDATE_REQ_SZ) {
-        return BLE_HS_EBADDATA;
+    if (op != proc->op) {
+        return 0;
     }
 
-    u8ptr = payload;
+    if (id != proc->id) {
+        return 0;
+    }
 
-    req->itvl_min = le16toh(u8ptr + 0);
-    req->itvl_max = le16toh(u8ptr + 2);
-    req->slave_latency = le16toh(u8ptr + 4);
-    req->timeout_multiplier = le16toh(u8ptr + 6);
+    if (expecting_only &&
+        !(proc->flags & BLE_L2CAP_SIG_PROC_F_EXPECTING)) {
 
-    return 0;
+        return 0;
+    }
+
+    return 1;
 }
 
-static int
-ble_l2cap_sig_update_rsp_write(void *payload, int len,
-                               struct ble_l2cap_sig_hdr *hdr,
-                               struct ble_l2cap_sig_update_rsp *cmd)
+/**
+ * Searched the global proc list for an entry that fits the specified criteria.
+ *
+ * @param conn_handle           The connection handle to match against.
+ * @param op                    The op code to match against.
+ * @param id                    The identifier to match against.
+ * @param expecting_only        1=Only match entries expecting a response;
+ *                                  0=Ignore this criterion.
+ * @param out_prev              On success, the address of the result's
+ *                                  previous entry gets written here.  Pass
+ *                                  null if you don't need this information.
+ *
+ * @return                      1 if the proc matches; 0 otherwise.
+ */
+static struct ble_l2cap_sig_proc *
+ble_l2cap_sig_proc_find(uint16_t conn_handle, uint8_t op, uint8_t id,
+                        int expecting_only,
+                        struct ble_l2cap_sig_proc **out_prev)
 {
-    uint8_t *u8ptr;
-    int rc;
-
-    u8ptr = payload;
-    rc = ble_l2cap_sig_hdr_write(u8ptr, len, hdr);
-    if (rc != 0) {
-        return rc;
+    struct ble_l2cap_sig_proc *proc;
+    struct ble_l2cap_sig_proc *prev;
+
+    prev = NULL;
+    STAILQ_FOREACH(proc, &ble_l2cap_sig_list, next) {
+        if (ble_l2cap_sig_proc_matches(proc, conn_handle, op, id,
+                                       expecting_only)) {
+            if (out_prev != NULL) {
+                *out_prev = prev;
+            }
+            return proc;
+        }
+
+        prev = proc;
     }
 
-    u8ptr += BLE_L2CAP_SIG_HDR_SZ;
-    len -= BLE_L2CAP_SIG_HDR_SZ;
+    return NULL;
+}
 
-    if (len < BLE_L2CAP_SIG_UPDATE_RSP_SZ) {
-        return BLE_HS_EMSGSIZE;
-    }
+/**
+ * Sets the specified proc entry's "pending" flag (i.e., indicates that the
+ * L2CAP sig procedure is stalled until it transmits its next request.
+ */
+static void
+ble_l2cap_sig_proc_set_pending(struct ble_l2cap_sig_proc *proc)
+{
+    assert(!(proc->flags & BLE_L2CAP_SIG_PROC_F_PENDING));
 
-    htole16(u8ptr, cmd->result);
+    proc->flags &= ~BLE_L2CAP_SIG_PROC_F_EXPECTING;
+    proc->flags |= BLE_L2CAP_SIG_PROC_F_PENDING;
+    ble_hs_kick_l2cap_sig();
+}
 
-    return 0;
+/**
+ * Sets the specified proc entry's "expecting" flag (i.e., indicates that the
+ * L2CAP sig procedure is stalled until it receives a response.
+ */
+static void
+ble_l2cap_sig_proc_set_expecting(struct ble_l2cap_sig_proc *proc,
+                                 struct ble_l2cap_sig_proc *prev)
+{
+    assert(!(proc->flags & BLE_L2CAP_SIG_PROC_F_EXPECTING));
+
+    ble_l2cap_sig_proc_remove(proc, prev);
+    proc->flags &= ~BLE_L2CAP_SIG_PROC_F_PENDING;
+    proc->flags |= BLE_L2CAP_SIG_PROC_F_EXPECTING;
+    proc->tx_time = os_time_get();
+    STAILQ_INSERT_TAIL(&ble_l2cap_sig_list, proc, next);
 }
 
 static int
-ble_l2cap_sig_update_rsp_tx(struct ble_hs_conn *conn,
-                            struct ble_l2cap_chan *chan,
-                            uint16_t identifier, uint16_t result)
+ble_l2cap_sig_rx_noop(struct ble_hs_conn *conn,
+                      struct ble_l2cap_chan *chan,
+                      struct ble_l2cap_sig_hdr *hdr,
+                      struct os_mbuf **om)
 {
-    struct ble_l2cap_sig_update_rsp rsp;
-    struct ble_l2cap_sig_hdr hdr;
-    struct os_mbuf *txom;
-    void *v;
-    int rc;
+    return 0;
+}
 
-    txom = ble_att_get_pkthdr();
-    if (txom == NULL) {
-        rc = BLE_HS_ENOMEM;
-        goto err;
-    }
+/*****************************************************************************
+ * $update                                                                   *
+ *****************************************************************************/
 
-    v = os_mbuf_extend(txom,
-                       BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ);
-    if (v == NULL) {
-        rc = BLE_HS_ENOMEM;
-        goto err;
+static void
+ble_l2cap_sig_update_call_cb(struct ble_l2cap_sig_proc *proc, int status)
+{
+    if (proc->update.cb != NULL) {
+        proc->update.cb(status, proc->update.cb_arg);
     }
-
-    hdr.op = BLE_L2CAP_SIG_OP_UPDATE_RSP;
-    hdr.identifier = identifier;
-    hdr.length = BLE_L2CAP_SIG_UPDATE_RSP_SZ;
-    rsp.result = result;
-
-    rc = ble_l2cap_sig_update_rsp_write(
-        v, BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ, &hdr, &rsp);
-    assert(rc == 0);
-
-    rc = ble_l2cap_tx(conn, chan, txom);
-    return rc;
-
-err:
-    os_mbuf_free_chain(txom);
-    return rc;
 }
 
 static int
@@ -245,6 +426,7 @@ ble_l2cap_sig_update_req_rx(struct ble_hs_conn *conn,
 {
     struct ble_l2cap_sig_update_req req;
     struct ble_gap_conn_upd_params params;
+    uint16_t l2cap_result;
     int rc;
 
     *om = os_mbuf_pullup(*om, BLE_L2CAP_SIG_UPDATE_REQ_SZ);
@@ -262,12 +444,6 @@ ble_l2cap_sig_update_req_rx(struct ble_hs_conn *conn,
         return BLE_HS_ENOTSUP;
     }
 
-    /* XXX: For now, always accept. */
-    rc = ble_l2cap_sig_update_rsp_tx(conn, chan, hdr->identifier, 0);
-    if (rc != 0) {
-        return rc;
-    }
-
     params.itvl_min = req.itvl_min;
     params.itvl_max = req.itvl_max;
     params.latency = req.slave_latency;
@@ -275,7 +451,117 @@ ble_l2cap_sig_update_req_rx(struct ble_hs_conn *conn,
     params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
     params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
 
-    rc = ble_gap_conn_update_params(conn->bhc_handle, &params);
+    rc = ble_gap_conn_rx_l2cap_update_req(conn, &params);
+    if (rc == 0) {
+        /* Application agrees to accept parameters; schedule update. */
+        rc = ble_gap_conn_update_params(conn->bhc_handle, &params);
+        if (rc != 0) {
+            return rc;
+        }
+        l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT;
+    } else {
+        l2cap_result = BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT;
+    }
+
+    /* Send L2CAP response. */
+    rc = ble_l2cap_sig_update_rsp_tx(conn, chan, hdr->identifier,
+                                     l2cap_result);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+ble_l2cap_sig_update_rsp_rx(struct ble_hs_conn *conn,
+                            struct ble_l2cap_chan *chan,
+                            struct ble_l2cap_sig_hdr *hdr,
+                            struct os_mbuf **om)
+{
+    struct ble_l2cap_sig_update_rsp rsp;
+    struct ble_l2cap_sig_proc *proc;
+    struct ble_l2cap_sig_proc *prev;
+    int cb_status;
+    int rc;
+
+    proc = ble_l2cap_sig_proc_find(conn->bhc_handle,
+                                   BLE_L2CAP_SIG_PROC_OP_UPDATE,
+                                   hdr->identifier, 1, &prev);
+    if (proc ==  NULL) {
+        return BLE_HS_ENOENT;
+    }
+
+    if (OS_MBUF_PKTLEN(*om) < BLE_L2CAP_SIG_UPDATE_RSP_SZ) {
+        cb_status = BLE_HS_EBADDATA;
+        rc = BLE_HS_EBADDATA;
+        goto done;
+    }
+
+    *om = os_mbuf_pullup(*om, BLE_L2CAP_SIG_UPDATE_RSP_SZ);
+    if (*om == NULL) {
+        cb_status = BLE_HS_ENOMEM;
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    rc = ble_l2cap_sig_update_rsp_parse((*om)->om_data, (*om)->om_len, &rsp);
+    assert(rc == 0);
+
+    switch (rsp.result) {
+    case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_ACCEPT:
+        cb_status = 0;
+        rc = 0;
+        break;
+
+    case BLE_L2CAP_SIG_UPDATE_RSP_RESULT_REJECT:
+        cb_status = BLE_HS_EREJECT;
+        rc = 0;
+        break;
+
+    default:
+        cb_status = BLE_HS_EBADDATA;
+        rc = BLE_HS_EBADDATA;
+        break;
+    }
+
+done:
+    ble_l2cap_sig_update_call_cb(proc, cb_status);
+    ble_l2cap_sig_proc_remove_free(proc, prev);
+    return rc;
+}
+
+static int
+ble_l2cap_sig_update_kick(struct ble_l2cap_sig_proc *proc)
+{
+    struct ble_l2cap_sig_update_req req;
+    struct ble_l2cap_chan *chan;
+    struct ble_hs_conn *conn;
+    int rc;
+
+    conn = ble_hs_conn_find(proc->conn_handle);
+    if (conn == NULL) {
+        /* Not connected; abort the proedure. */
+        ble_l2cap_sig_update_call_cb(proc, BLE_HS_ENOTCONN);
+        return BLE_HS_EDONE;
+    }
+
+    /* Only the slave can initiate the L2CAP connection update procedure. */
+    if (conn->bhc_flags & BLE_HS_CONN_F_MASTER) {
+        ble_l2cap_sig_update_call_cb(proc, BLE_HS_EINVAL);
+        return BLE_HS_EDONE;
+    }
+
+    chan = ble_hs_conn_chan_find(conn, BLE_L2CAP_CID_SIG);
+    assert(chan != NULL);
+
+    proc->id = ble_l2cap_sig_next_id();
+    req.itvl_min = proc->update.params.itvl_min;
+    req.itvl_max = proc->update.params.itvl_max;
+    req.slave_latency = proc->update.params.slave_latency;
+    req.timeout_multiplier = proc->update.params.timeout_multiplier;
+
+    rc = ble_l2cap_sig_update_req_tx(conn, chan, proc->id, &req);
     if (rc != 0) {
         return rc;
     }
@@ -283,6 +569,28 @@ ble_l2cap_sig_update_req_rx(struct ble_hs_conn *conn,
     return 0;
 }
 
+int
+ble_l2cap_sig_update(uint16_t conn_handle,
+                     struct ble_l2cap_sig_update_params *params,
+                     ble_l2cap_sig_update_fn *cb, void *cb_arg)
+{
+    struct ble_l2cap_sig_proc *proc;
+    int rc;
+
+    rc = ble_l2cap_sig_new_proc(conn_handle, BLE_L2CAP_SIG_PROC_OP_UPDATE,
+                                &proc);
+    if (rc != 0) {
+        return rc;
+    }
+
+    proc->update.params = *params;
+    proc->update.cb = cb;
+    proc->update.cb_arg = cb_arg;
+
+    ble_l2cap_sig_proc_set_pending(proc);
+
+    return 0;
+}
 
 static int
 ble_l2cap_sig_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
@@ -290,15 +598,10 @@ ble_l2cap_sig_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
 {
     ble_l2cap_sig_rx_fn *rx_cb;
     struct ble_l2cap_sig_hdr hdr;
-    uint8_t u8;
     int rc;
-    int i;
 
     console_printf("L2CAP - rxed signalling msg: ");
-    for (i = 0; i < OS_MBUF_PKTLEN(*om); i++) {
-        os_mbuf_copydata(*om, i, 1, &u8);
-        console_printf("0x%02x ", u8);
-    }
+    ble_hs_misc_log_mbuf(*om);
     console_printf("\n");
 
     *om = os_mbuf_pullup(*om, BLE_L2CAP_SIG_HDR_SZ);
@@ -345,3 +648,130 @@ ble_l2cap_sig_create_chan(void)
 
     return chan;
 }
+
+/**
+ * Applies periodic checks and actions to all active procedures.
+ *
+ * All procedures that failed due to memory exaustion have their pending flag
+ * set so they can be retried.
+ *
+ * All procedures that have been expecting a response for longer than five
+ * seconds are aborted, and their corresponding connection is terminated.
+ *
+ * Called by the heartbeat timer; executed every second.
+ */
+static void
+ble_l2cap_sig_heartbeat(void *unused)
+{
+    struct ble_l2cap_sig_proc *proc;
+    uint32_t now;
+    int rc;
+
+    now = os_time_get();
+
+    STAILQ_FOREACH(proc, &ble_l2cap_sig_list, next) {
+        if (proc->flags & BLE_L2CAP_SIG_PROC_F_NO_MEM) {
+            proc->flags &= ~BLE_L2CAP_SIG_PROC_F_NO_MEM;
+            if (ble_l2cap_sig_proc_can_pend(proc)) {
+                ble_l2cap_sig_proc_set_pending(proc);
+            }
+        } else if (proc->flags & BLE_L2CAP_SIG_PROC_F_EXPECTING) {
+            if (now - proc->tx_time >= BLE_L2CAP_SIG_UNRESPONSIVE_TIMEOUT) {
+                rc = ble_gap_conn_terminate(proc->conn_handle);
+                assert(rc == 0); /* XXX */
+            }
+        }
+    }
+
+    rc = os_callout_reset(
+        &ble_l2cap_sig_heartbeat_timer.cf_c,
+        BLE_L2CAP_SIG_HEARTBEAT_PERIOD * OS_TICKS_PER_SEC / 1000);
+    assert(rc == 0);
+}
+
+void
+ble_l2cap_sig_wakeup(void)
+{
+    struct ble_l2cap_sig_proc *proc;
+    struct ble_l2cap_sig_proc *prev;
+    struct ble_l2cap_sig_proc *next;
+    ble_l2cap_sig_kick_fn *kick_cb;
+    int rc;
+
+    prev = NULL;
+    proc = STAILQ_FIRST(&ble_l2cap_sig_list);
+    while (proc != NULL) {
+        next = STAILQ_NEXT(proc, next);
+
+        if (proc->flags & BLE_L2CAP_SIG_PROC_F_PENDING) {
+            kick_cb = ble_l2cap_sig_kick_get(proc->op);
+            rc = kick_cb(proc);
+            switch (rc) {
+            case 0:
+                /* Transmit succeeded.  Response expected. */
+                ble_l2cap_sig_proc_set_expecting(proc, prev);
+                /* Current proc got moved to back; old prev still valid. */
+                break;
+
+            case BLE_HS_EAGAIN:
+                /* Transmit failed due to resource shortage.  Reschedule. */
+                proc->flags &= ~BLE_L2CAP_SIG_PROC_F_PENDING;
+                /* Current proc remains; reseat prev. */
+                prev = proc;
+                break;
+
+            case BLE_HS_EDONE:
+                /* Procedure complete. */
+                ble_l2cap_sig_proc_remove_free(proc, prev);
+                /* Current proc removed; old prev still valid. */
+                break;
+
+            default:
+                assert(0);
+                break;
+            }
+        } else {
+            prev = proc;
+        }
+
+        proc = next;
+    }
+
+    ble_l2cap_sig_assert_sanity();
+}
+
+int
+ble_l2cap_sig_init(void)
+{
+    int rc;
+
+    free(ble_l2cap_sig_proc_mem);
+
+    ble_l2cap_sig_proc_mem = malloc(
+        OS_MEMPOOL_BYTES(BLE_L2CAP_SIG_NUM_PROCS,
+                         sizeof (struct ble_l2cap_sig_proc)));
+    if (ble_l2cap_sig_proc_mem == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    rc = os_mempool_init(&ble_l2cap_sig_proc_pool,
+                         BLE_L2CAP_SIG_NUM_PROCS,
+                         sizeof (struct ble_l2cap_sig_proc),
+                         ble_l2cap_sig_proc_mem,
+                         "ble_l2cap_sig_proc_pool");
+    if (rc != 0) {
+        goto err;
+    }
+
+    STAILQ_INIT(&ble_l2cap_sig_list);
+
+    os_callout_func_init(&ble_l2cap_sig_heartbeat_timer, &ble_hs_evq,
+                         ble_l2cap_sig_heartbeat, NULL);
+
+    return 0;
+
+err:
+    free(ble_l2cap_sig_proc_mem);
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/ble_l2cap_sig_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sig_cmd.c b/net/nimble/host/src/ble_l2cap_sig_cmd.c
new file mode 100644
index 0000000..7f62ce0
--- /dev/null
+++ b/net/nimble/host/src/ble_l2cap_sig_cmd.c
@@ -0,0 +1,277 @@
+#include <assert.h>
+#include "ble_hs_priv.h"
+#include "ble_att_priv.h"
+#include "ble_hs_conn.h"
+#include "ble_l2cap_priv.h"
+
+int
+ble_l2cap_sig_hdr_parse(void *payload, uint16_t len,
+                        struct ble_l2cap_sig_hdr *hdr)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_L2CAP_SIG_HDR_SZ) {
+        return BLE_HS_EBADDATA;
+    }
+
+    u8ptr = payload;
+    hdr->op = u8ptr[0];
+    hdr->identifier = u8ptr[1];
+    hdr->length = le16toh(u8ptr + 2);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_hdr_write(void *payload, uint16_t len,
+                        struct ble_l2cap_sig_hdr *hdr)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_L2CAP_SIG_HDR_SZ) {
+        return BLE_HS_EBADDATA;
+    }
+
+    u8ptr = payload;
+    u8ptr[0] = hdr->op;
+    u8ptr[1] = hdr->identifier;
+    htole16(u8ptr + 2, hdr->length);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_reject_write(void *payload, uint16_t len,
+                           struct ble_l2cap_sig_hdr *hdr,
+                           struct ble_l2cap_sig_reject *cmd)
+{
+    uint8_t *u8ptr;
+    int rc;
+
+    u8ptr = payload;
+    rc = ble_l2cap_sig_hdr_write(u8ptr, len, hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    u8ptr += BLE_L2CAP_SIG_HDR_SZ;
+    len -= BLE_L2CAP_SIG_HDR_SZ;
+
+    if (len < BLE_L2CAP_SIG_REJECT_MIN_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    htole16(u8ptr, cmd->reason);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_reject_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
+                        uint8_t id, uint16_t reason)
+{
+    /* XXX: Add support for optional data field. */
+
+    struct ble_l2cap_sig_reject cmd;
+    struct ble_l2cap_sig_hdr hdr;
+    struct os_mbuf *txom;
+    void *v;
+    int rc;
+
+    txom = ble_att_get_pkthdr();
+    if (txom == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    v = os_mbuf_extend(txom,
+                       BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_REJECT_MIN_SZ);
+    if (v == NULL) {
+        return BLE_HS_ENOMEM;
+    }
+
+    hdr.op = BLE_L2CAP_SIG_OP_REJECT;
+    hdr.identifier = id;
+    hdr.length = BLE_L2CAP_SIG_REJECT_MIN_SZ;
+    cmd.reason = reason;
+
+    rc = ble_l2cap_sig_reject_write(
+        v, BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_REJECT_MIN_SZ, &hdr, &cmd);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_l2cap_tx(conn, chan, txom);
+    return rc;
+}
+
+int
+ble_l2cap_sig_update_req_parse(void *payload, int len,
+                               struct ble_l2cap_sig_update_req *req)
+{
+    uint8_t *u8ptr;
+
+    if (len < BLE_L2CAP_SIG_UPDATE_REQ_SZ) {
+        return BLE_HS_EBADDATA;
+    }
+
+    u8ptr = payload;
+
+    req->itvl_min = le16toh(u8ptr + 0);
+    req->itvl_max = le16toh(u8ptr + 2);
+    req->slave_latency = le16toh(u8ptr + 4);
+    req->timeout_multiplier = le16toh(u8ptr + 6);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_update_req_write(void *payload, int len,
+                               struct ble_l2cap_sig_hdr *hdr,
+                               struct ble_l2cap_sig_update_req *req)
+{
+    uint8_t *u8ptr;
+    int rc;
+
+    u8ptr = payload;
+    rc = ble_l2cap_sig_hdr_write(u8ptr, len, hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    u8ptr += BLE_L2CAP_SIG_HDR_SZ;
+    len -= BLE_L2CAP_SIG_HDR_SZ;
+
+    if (len < BLE_L2CAP_SIG_UPDATE_REQ_SZ) {
+        return BLE_HS_EINVAL;
+    }
+
+    htole16(u8ptr + 0, req->itvl_min);
+    htole16(u8ptr + 2, req->itvl_max);
+    htole16(u8ptr + 4, req->slave_latency);
+    htole16(u8ptr + 6, req->timeout_multiplier);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_update_rsp_parse(void *payload, int len,
+                               struct ble_l2cap_sig_update_rsp *cmd)
+{
+    uint8_t *u8ptr;
+
+    u8ptr = payload;
+
+    if (len < BLE_L2CAP_SIG_UPDATE_RSP_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    cmd->result = le16toh(u8ptr);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_update_rsp_write(void *payload, int len,
+                               struct ble_l2cap_sig_hdr *hdr,
+                               struct ble_l2cap_sig_update_rsp *cmd)
+{
+    uint8_t *u8ptr;
+    int rc;
+
+    u8ptr = payload;
+    rc = ble_l2cap_sig_hdr_write(u8ptr, len, hdr);
+    if (rc != 0) {
+        return rc;
+    }
+
+    u8ptr += BLE_L2CAP_SIG_HDR_SZ;
+    len -= BLE_L2CAP_SIG_HDR_SZ;
+
+    if (len < BLE_L2CAP_SIG_UPDATE_RSP_SZ) {
+        return BLE_HS_EMSGSIZE;
+    }
+
+    htole16(u8ptr, cmd->result);
+
+    return 0;
+}
+
+int
+ble_l2cap_sig_update_req_tx(struct ble_hs_conn *conn,
+                            struct ble_l2cap_chan *chan, uint8_t id,
+                            struct ble_l2cap_sig_update_req *req)
+{
+    struct ble_l2cap_sig_hdr hdr;
+    struct os_mbuf *txom;
+    void *v;
+    int rc;
+
+    txom = ble_att_get_pkthdr();
+    if (txom == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    v = os_mbuf_extend(txom,
+                       BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_REQ_SZ);
+    if (v == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto done;
+    }
+
+    hdr.op = BLE_L2CAP_SIG_OP_UPDATE_REQ;
+    hdr.identifier = id;
+    hdr.length = BLE_L2CAP_SIG_UPDATE_REQ_SZ;
+
+    rc = ble_l2cap_sig_update_req_write(
+        v, BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_REQ_SZ, &hdr, req);
+    assert(rc == 0);
+
+    rc = ble_l2cap_tx(conn, chan, txom);
+    txom = NULL;
+
+done:
+    os_mbuf_free_chain(txom);
+    return rc;
+}
+
+int
+ble_l2cap_sig_update_rsp_tx(struct ble_hs_conn *conn,
+                            struct ble_l2cap_chan *chan, uint8_t id,
+                            uint16_t result)
+{
+    struct ble_l2cap_sig_update_rsp rsp;
+    struct ble_l2cap_sig_hdr hdr;
+    struct os_mbuf *txom;
+    void *v;
+    int rc;
+
+    txom = ble_att_get_pkthdr();
+    if (txom == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    v = os_mbuf_extend(txom,
+                       BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ);
+    if (v == NULL) {
+        rc = BLE_HS_ENOMEM;
+        goto err;
+    }
+
+    hdr.op = BLE_L2CAP_SIG_OP_UPDATE_RSP;
+    hdr.identifier = id;
+    hdr.length = BLE_L2CAP_SIG_UPDATE_RSP_SZ;
+    rsp.result = result;
+
+    rc = ble_l2cap_sig_update_rsp_write(
+        v, BLE_L2CAP_SIG_HDR_SZ + BLE_L2CAP_SIG_UPDATE_RSP_SZ, &hdr, &rsp);
+    assert(rc == 0);
+
+    rc = ble_l2cap_tx(conn, chan, txom);
+    return rc;
+
+err:
+    os_mbuf_free_chain(txom);
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/host_hci.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/host_hci.c b/net/nimble/host/src/host_hci.c
index 607fb0a..863125f 100644
--- a/net/nimble/host/src/host_hci.c
+++ b/net/nimble/host/src/host_hci.c
@@ -26,7 +26,7 @@
 #include "ble_hs_priv.h"
 #include "host_dbg.h"
 #include "ble_hs_conn.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_hci_ack.h"
 #include "ble_gap_priv.h"
 #include "ble_hs_adv_priv.h"
@@ -650,19 +650,6 @@ host_hci_data_hdr_strip(struct os_mbuf *om, struct hci_data_hdr *hdr)
     return 0;
 }
 
-static void
-host_hci_log_pkt(struct os_mbuf *om)
-{
-    uint8_t u8;
-    int i;
-
-    for (i = 0; i < OS_MBUF_PKTLEN(om); i++) {
-        os_mbuf_copydata(om, i, 1, &u8);
-        console_printf("0x%02x ", u8);
-    }
-    console_printf("\n");
-}
-
 /**
  * Called when a data packet is received from the controller.  This function
  * consumes the supplied mbuf, regardless of the outcome.
@@ -689,7 +676,7 @@ host_hci_data_rx(struct os_mbuf *om)
                    BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc), 
                    BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc), 
                    hci_hdr.hdh_len);
-    host_hci_log_pkt(om);
+    ble_hs_misc_log_mbuf(om);
 
     if (hci_hdr.hdh_len != OS_MBUF_PKTHDR(om)->omp_len) {
         rc = BLE_HS_EMSGSIZE;
@@ -763,7 +750,7 @@ host_hci_data_tx(struct ble_hs_conn *connection, struct os_mbuf *om)
     }
 
     console_printf("host_hci_data_tx(): ");
-    host_hci_log_pkt(om);
+    ble_hs_misc_log_mbuf(om);
 
     rc = ble_hs_tx_data(om);
     if (rc != 0) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/host_hci_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/host_hci_cmd.c b/net/nimble/host/src/host_hci_cmd.c
index 99b5660..684b512 100644
--- a/net/nimble/host/src/host_hci_cmd.c
+++ b/net/nimble/host/src/host_hci_cmd.c
@@ -27,7 +27,7 @@
 #include "host_dbg.h"
 #include "ble_hci_ack.h"
 #include "ble_hs_conn.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #ifdef PHONY_TRANSPORT
 #include "host/ble_hs_test.h"
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 da56892..b3de244 100644
--- a/net/nimble/host/src/test/ble_att_svr_test.c
+++ b/net/nimble/host/src/test/ble_att_svr_test.c
@@ -22,7 +22,7 @@
 #include "host/ble_hs_test.h"
 #include "host/ble_uuid.h"
 #include "testutil/testutil.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_hs_test_util.h"
 #include "ble_hs_conn.h"
 #include "ble_att_priv.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/test/ble_host_hci_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_host_hci_test.c b/net/nimble/host/src/test/ble_host_hci_test.c
index 284f149..66ce171 100644
--- a/net/nimble/host/src/test/ble_host_hci_test.c
+++ b/net/nimble/host/src/test/ble_host_hci_test.c
@@ -22,7 +22,7 @@
 #include "ble_hs_priv.h"
 #include "host/ble_hs_test.h"
 #include "testutil/testutil.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_hs_conn.h"
 #include "ble_att_priv.h"
 #include "ble_att_cmd.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 082bd2e..fed7cf2 100644
--- a/net/nimble/host/src/test/ble_hs_adv_test.c
+++ b/net/nimble/host/src/test/ble_hs_adv_test.c
@@ -21,7 +21,7 @@
 #include "ble_hs_priv.h"
 #include "host/ble_hs_test.h"
 #include "host/host_hci.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_priv.h"
 #include "ble_hs_conn.h"
 #include "ble_hs_adv_priv.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/net/nimble/host/src/test/ble_hs_conn_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_conn_test.c b/net/nimble/host/src/test/ble_hs_conn_test.c
index 504504f..a88e994 100644
--- a/net/nimble/host/src/test/ble_hs_conn_test.c
+++ b/net/nimble/host/src/test/ble_hs_conn_test.c
@@ -21,7 +21,7 @@
 #include "ble_hs_priv.h"
 #include "host/ble_hs_test.h"
 #include "host/host_hci.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_priv.h"
 #include "ble_hs_conn.h"
 #include "ble_hci_ack.h"

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 3ebc521..d46a2e2 100644
--- a/net/nimble/host/src/test/ble_hs_test.c
+++ b/net/nimble/host/src/test/ble_hs_test.c
@@ -19,7 +19,7 @@
 #include "ble_hs_priv.h"
 #include "host/ble_hs_test.h"
 #include "testutil/testutil.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_hs_test_util.h"
 
 void

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/894a15c3/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 68041c4..902653c 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.c
+++ b/net/nimble/host/src/test/ble_hs_test_util.c
@@ -26,7 +26,7 @@
 #include "ble_hci_sched.h"
 #include "ble_hs_conn.h"
 #include "ble_gap_priv.h"
-#include "ble_l2cap.h"
+#include "ble_l2cap_priv.h"
 #include "ble_att_cmd.h"
 #include "ble_hs_test_util.h"
 
@@ -362,6 +362,7 @@ void
 ble_hs_test_util_tx_all(void)
 {
     ble_gattc_wakeup();
+    ble_l2cap_sig_wakeup();
     ble_hs_process_tx_data_queue();
 }