You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/04/28 02:14:08 UTC

[09/50] [abbrv] incubator-mynewt-core git commit: ble host - major changes.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e32f9f9f/net/nimble/host/src/ble_l2cap_sm.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm.c b/net/nimble/host/src/ble_l2cap_sm.c
index d53124f..a2e5785 100644
--- a/net/nimble/host/src/ble_l2cap_sm.c
+++ b/net/nimble/host/src/ble_l2cap_sm.c
@@ -26,18 +26,14 @@
 
 #if NIMBLE_OPT_SM
 
-#define BLE_L2CAP_SM_PROC_OP_NONE               ((uint8_t)-1)
-
-#define BLE_L2CAP_SM_PROC_OP_PAIR               0
-#define BLE_L2CAP_SM_PROC_OP_CONFIRM            1
-#define BLE_L2CAP_SM_PROC_OP_RANDOM             2
-#define BLE_L2CAP_SM_PROC_OP_FAIL               3
-#define BLE_L2CAP_SM_PROC_OP_LTK                4
-#define BLE_L2CAP_SM_PROC_OP_LTK_TXED           5
-#define BLE_L2CAP_SM_PROC_OP_ENC_CHANGE         6
-#define BLE_L2CAP_SM_PROC_OP_START_ENCRYPT      7
-#define BLE_L2CAP_SM_PROC_OP_START_ENCRYPT_TXED 8
-#define BLE_L2CAP_SM_PROC_OP_CNT                9
+#define BLE_L2CAP_SM_PROC_STATE_NONE            ((uint8_t)-1)
+
+#define BLE_L2CAP_SM_PROC_STATE_PAIR            0
+#define BLE_L2CAP_SM_PROC_STATE_CONFIRM         1
+#define BLE_L2CAP_SM_PROC_STATE_RANDOM          2
+#define BLE_L2CAP_SM_PROC_STATE_LTK             3
+#define BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE      4
+#define BLE_L2CAP_SM_PROC_STATE_CNT             5
 
 #define BLE_L2CAP_SM_PROC_F_INITIATOR           0x01
 
@@ -47,10 +43,14 @@
 typedef uint16_t ble_l2cap_sm_proc_flags;
 
 struct ble_l2cap_sm_proc {
-    struct ble_fsm_proc fsm_proc;
+    STAILQ_ENTRY(ble_l2cap_sm_proc) next;
+
     uint32_t exp_os_ticks;
     ble_l2cap_sm_proc_flags flags;
+    uint16_t conn_handle;
     uint8_t pair_alg;
+    uint8_t state;
+
 
     /* XXX: Minimum security requirements. */
 
@@ -66,25 +66,13 @@ struct ble_l2cap_sm_proc {
 
         struct {
             uint8_t key[16];
-            uint8_t handle;
         } hci;
-
-        struct {
-            uint8_t reason;
-        } fail;
     };
 };
 
-/** Used for extracting proc entries from the fsm list. */
-struct ble_l2cap_sm_extract_arg {
-    uint16_t conn_handle;
-    uint8_t op;
-    int8_t initiator; /* 0=no, 1=yes, -1=don't-care. */
-};
-
-typedef int ble_l2cap_sm_kick_fn(struct ble_l2cap_sm_proc *proc);
+STAILQ_HEAD(ble_l2cap_sm_proc_list, ble_l2cap_sm_proc);
 
-typedef int ble_l2cap_sm_rx_fn(uint16_t conn_handle, uint8_t op,
+typedef int ble_l2cap_sm_rx_fn(uint16_t conn_handle, uint8_t state,
                                struct os_mbuf **om);
 
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_noop;
@@ -94,13 +82,6 @@ static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_confirm;
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_random;
 static ble_l2cap_sm_rx_fn ble_l2cap_sm_rx_pair_fail;
 
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_pair_kick;
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_confirm_kick;
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_random_kick;
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_fail_kick;
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_lt_key_req_kick;
-static ble_l2cap_sm_kick_fn ble_l2cap_sm_start_encrypt_kick;
-
 static ble_l2cap_sm_rx_fn * const ble_l2cap_sm_dispatch[] = {
    [BLE_L2CAP_SM_OP_PAIR_REQ] = ble_l2cap_sm_rx_pair_req,
    [BLE_L2CAP_SM_OP_PAIR_RSP] = ble_l2cap_sm_rx_pair_rsp,
@@ -118,31 +99,24 @@ static ble_l2cap_sm_rx_fn * const ble_l2cap_sm_dispatch[] = {
    [BLE_L2CAP_SM_OP_PAIR_KEYPRESS_NOTIFY] = ble_l2cap_sm_rx_noop,
 };
 
-static ble_l2cap_sm_kick_fn * const
-ble_l2cap_sm_kick[BLE_L2CAP_SM_PROC_OP_CNT] = {
-    [BLE_L2CAP_SM_PROC_OP_PAIR]             = ble_l2cap_sm_pair_kick,
-    [BLE_L2CAP_SM_PROC_OP_CONFIRM]          = ble_l2cap_sm_confirm_kick,
-    [BLE_L2CAP_SM_PROC_OP_RANDOM]           = ble_l2cap_sm_random_kick,
-    [BLE_L2CAP_SM_PROC_OP_FAIL]             = ble_l2cap_sm_fail_kick,
-    [BLE_L2CAP_SM_PROC_OP_LTK]              = ble_l2cap_sm_lt_key_req_kick,
-    [BLE_L2CAP_SM_PROC_OP_START_ENCRYPT]    = ble_l2cap_sm_start_encrypt_kick,
-};
-
-static void ble_l2cap_sm_rx_lt_key_req_reply_ack(struct ble_hci_ack *ack,
-                                                 void *arg);
-static void ble_l2cap_sm_rx_start_encrypt_ack(struct ble_hci_ack *ack,
-                                              void *arg);
-
 static void *ble_l2cap_sm_proc_mem;
 static struct os_mempool ble_l2cap_sm_proc_pool;
 
-static struct ble_fsm ble_l2cap_sm_fsm;
+/* Maintains the list of active security manager procedures. */
+static struct ble_l2cap_sm_proc_list ble_l2cap_sm_procs;
+
+static int ble_l2cap_sm_confirm_prepare_args(struct ble_l2cap_sm_proc *proc,
+                                             uint8_t *k, uint8_t *preq,
+                                             uint8_t *pres, uint8_t *iat,
+                                             uint8_t *rat, uint8_t *ia,
+                                             uint8_t *ra);
 
 /*****************************************************************************
  * $debug                                                                    *
  *****************************************************************************/
 
 #ifdef BLE_HS_DEBUG
+
 static uint8_t ble_l2cap_sm_dbg_next_pair_rand[16];
 static uint8_t ble_l2cap_sm_dbg_next_pair_rand_set;
 static uint16_t ble_l2cap_sm_dbg_next_ediv;
@@ -175,135 +149,122 @@ ble_l2cap_sm_dbg_set_next_start_rand(uint64_t next_start_rand)
 int
 ble_l2cap_sm_dbg_num_procs(void)
 {
-    struct ble_fsm_proc *proc;
+    struct ble_l2cap_sm_proc *proc;
     int cnt;
 
-    ble_fsm_lock(&ble_l2cap_sm_fsm);
-
     cnt = 0;
-    STAILQ_FOREACH(proc, &ble_l2cap_sm_fsm.procs, next) {
+    STAILQ_FOREACH(proc, &ble_l2cap_sm_procs, next) {
         cnt++;
     }
 
-    ble_fsm_unlock(&ble_l2cap_sm_fsm);
-
     return cnt;
 }
 
 #endif
 
 /*****************************************************************************
- * $mutex                                                                    *
- *****************************************************************************/
-
-int
-ble_l2cap_sm_locked_by_cur_task(void)
-{
-    return ble_fsm_locked_by_cur_task(&ble_l2cap_sm_fsm);
-}
-
-/*****************************************************************************
  * $misc                                                                     *
  *****************************************************************************/
 
-static void
+static int
 ble_l2cap_sm_gen_pair_rand(uint8_t *pair_rand)
 {
+    int rc;
+
 #ifdef BLE_HS_DEBUG
     if (ble_l2cap_sm_dbg_next_pair_rand_set) {
         ble_l2cap_sm_dbg_next_pair_rand_set = 0;
         memcpy(pair_rand, ble_l2cap_sm_dbg_next_pair_rand,
                sizeof ble_l2cap_sm_dbg_next_pair_rand);
-        return;
+        return 0;
     }
 #endif
 
-    /* XXX: Generate random value rather than zeros. */
-    memset(pair_rand, 0, 16);
+    rc = ble_hci_util_rand(pair_rand, 16);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
 }
 
-static uint16_t
-ble_l2cap_sm_gen_ediv(void)
+static int
+ble_l2cap_sm_gen_ediv(uint16_t *ediv)
 {
+    int rc;
+
 #ifdef BLE_HS_DEBUG
     if (ble_l2cap_sm_dbg_next_ediv_set) {
         ble_l2cap_sm_dbg_next_ediv_set = 0;
-        return ble_l2cap_sm_dbg_next_ediv;
+        *ediv = ble_l2cap_sm_dbg_next_ediv;
+        return 0;
     }
 #endif
 
-    /* XXX: Generate random value rather than zero. */
+    rc = ble_hci_util_rand(ediv, sizeof *ediv);
+    if (rc != 0) {
+        return rc;
+    }
+
     return 0;
 }
 
-static uint64_t
-ble_l2cap_sm_gen_start_rand(void)
+static int
+ble_l2cap_sm_gen_start_rand(uint64_t *start_rand)
 {
+    int rc;
+
 #ifdef BLE_HS_DEBUG
     if (ble_l2cap_sm_dbg_next_start_rand_set) {
         ble_l2cap_sm_dbg_next_start_rand_set = 0;
-        return ble_l2cap_sm_dbg_next_start_rand;
+        *start_rand = ble_l2cap_sm_dbg_next_start_rand;
+        return 0;
     }
 #endif
 
-    /* XXX: Generate random value rather than zeros. */
+    rc = ble_hci_util_rand(start_rand, sizeof *start_rand);
+    if (rc != 0) {
+        return rc;
+    }
+
     return 0;
 }
 
 static int
-ble_l2cap_sm_proc_kick(struct ble_fsm_proc *proc)
+ble_l2cap_sm_gen_key(struct ble_l2cap_sm_proc *proc)
 {
-    struct ble_l2cap_sm_proc *sm_proc;
-    ble_l2cap_sm_kick_fn *kick_cb;
+    uint8_t key[16];
     int rc;
 
-    BLE_HS_DBG_ASSERT(proc->op < BLE_L2CAP_SM_PROC_OP_CNT);
-    kick_cb = ble_l2cap_sm_kick[proc->op];
-    BLE_HS_DBG_ASSERT(kick_cb != NULL);
+    rc = ble_l2cap_sm_alg_s1(proc->phase_1_2.tk, proc->phase_1_2.rand_our,
+                             proc->phase_1_2.rand_their, key);
+    if (rc != 0) {
+        return rc;
+    }
 
-    /* Set a timeout of 30 seconds. */
-    sm_proc = (struct ble_l2cap_sm_proc *)proc;
+    memcpy(proc->hci.key, key, sizeof key);
 
-    sm_proc->exp_os_ticks = os_time_get() + BLE_L2CAP_SM_TIMEOUT_OS_TICKS;
-    rc = kick_cb(sm_proc);
+    return 0;
+}
 
-    return rc;
+static void
+ble_l2cap_sm_proc_set_timer(struct ble_l2cap_sm_proc *proc)
+{
+    /* Set a timeout of 30 seconds. */
+    proc->exp_os_ticks = os_time_get() + BLE_L2CAP_SM_TIMEOUT_OS_TICKS;
 }
 
 /**
  * Lock restrictions: None.
  */
 static ble_l2cap_sm_rx_fn *
-ble_l2cap_sm_dispatch_get(uint8_t op)
+ble_l2cap_sm_dispatch_get(uint8_t state)
 {
-    if (op > sizeof ble_l2cap_sm_dispatch / sizeof ble_l2cap_sm_dispatch[0]) {
+    if (state > sizeof ble_l2cap_sm_dispatch / sizeof ble_l2cap_sm_dispatch[0]) {
         return NULL;
     }
 
-    return ble_l2cap_sm_dispatch[op];
-}
-
-/* Indicates the handle of the specified proc's unserviced HCI reservation, if
- * any.  If there is no such handle associated with the proc,
- * BLE_HCI_SCHED_HANDLE_NONE is returned.
- *
- * Lock restrictions: None.
- *
- * @param proc                  The proc object to query.
- *
- * @return                      The HCI handle, or BLE_HCI_SCHED_HANDLE_NONE.
- */
-static uint8_t
-ble_l2cap_sm_proc_outstanding_hci_handle(struct ble_l2cap_sm_proc *proc)
-{
-    switch (proc->fsm_proc.op) {
-    case BLE_L2CAP_SM_PROC_OP_LTK:
-    case BLE_L2CAP_SM_PROC_OP_START_ENCRYPT_TXED:
-        return proc->hci.handle;
-
-    default:
-        return BLE_HCI_SCHED_HANDLE_NONE;
-    }
+    return ble_l2cap_sm_dispatch[state];
 }
 
 /**
@@ -327,269 +288,336 @@ ble_l2cap_sm_proc_alloc(void)
 }
 
 /**
- * Frees the specified proc entry.  No-op if passed a null pointer.
+ * Frees the specified proc entry.  No-state if passed a null pointer.
  *
  * Lock restrictions: None.
  */
 static void
-ble_l2cap_sm_proc_free(struct ble_fsm_proc *proc)
+ble_l2cap_sm_proc_free(struct ble_l2cap_sm_proc *proc)
 {
-    struct ble_l2cap_sm_proc *sm_proc;
-    uint8_t hci_handle;
     int rc;
 
     if (proc != NULL) {
-        sm_proc = (struct ble_l2cap_sm_proc *)proc;
-
-        /* If this proc has an unserviced HCI reservation, cancel it before
-         * freeing the proc.
-         */
-        hci_handle = ble_l2cap_sm_proc_outstanding_hci_handle(sm_proc);
-        if (hci_handle != BLE_HCI_SCHED_HANDLE_NONE) {
-            rc = ble_hci_sched_cancel(hci_handle);
-            BLE_HS_DBG_ASSERT_EVAL(rc == 0);
-        }
-
         rc = os_memblock_put(&ble_l2cap_sm_proc_pool, proc);
         BLE_HS_DBG_ASSERT_EVAL(rc == 0);
     }
 }
 
-/**
- * Lock restrictions: None.
- */
-static int
-ble_l2cap_sm_proc_new(uint16_t conn_handle, uint8_t op,
-                      struct ble_l2cap_sm_proc **out_proc)
+static void
+ble_l2cap_sm_proc_insert(struct ble_l2cap_sm_proc *proc)
 {
-    *out_proc = ble_l2cap_sm_proc_alloc();
-    if (*out_proc == NULL) {
-        return BLE_HS_ENOMEM;
-    }
-
-    memset(*out_proc, 0, sizeof **out_proc);
-    (*out_proc)->fsm_proc.op = op;
-    (*out_proc)->fsm_proc.conn_handle = conn_handle;
-    (*out_proc)->fsm_proc.tx_time = os_time_get();
+    ble_hs_lock();
+    STAILQ_INSERT_HEAD(&ble_l2cap_sm_procs, proc, next);
+    ble_hs_unlock();
+}
 
-    return 0;
+static void
+ble_l2cap_sm_sec_params(struct ble_l2cap_sm_proc *proc,
+                        struct ble_gap_sec_params *out_sec_params,
+                        int enc_enabled)
+{
+    out_sec_params->pair_alg = proc->pair_alg;
+    out_sec_params->enc_enabled = enc_enabled;
+    out_sec_params->auth_enabled = 0; // XXX
 }
 
-/**
- * Extraction callback used for removing all pairing procedures that have timed
- * out.
- */
-static int
-ble_l2cap_sm_proc_extract_expired_cb(struct ble_fsm_proc *proc, void *unused)
+static void
+ble_l2cap_sm_gap_event(struct ble_l2cap_sm_proc *proc, int status,
+                       int enc_enabled)
 {
-    uint32_t now;
-    int32_t diff;
+    struct ble_gap_sec_params sec_params;
 
-    now = os_time_get();
-    diff = now - ((struct ble_l2cap_sm_proc *)proc)->exp_os_ticks;
-    if (diff >= 0) {
-        return BLE_FSM_EXTRACT_EMOVE_CONTINUE;
+    ble_l2cap_sm_sec_params(proc, &sec_params, enc_enabled);
+    ble_gap_security_event(proc->conn_handle, status, &sec_params);
+}
+
+static void
+ble_l2cap_sm_process_status(struct ble_l2cap_sm_proc *proc, int status,
+                            uint8_t sm_status, int call_cb, int tx_fail)
+{
+    if (status == 0) {
+        ble_l2cap_sm_proc_insert(proc);
     } else {
-        return BLE_FSM_EXTRACT_EKEEP_CONTINUE;
+        if (tx_fail) {
+            ble_l2cap_sm_pair_fail_tx(proc->conn_handle, sm_status);
+        }
+        if (call_cb) {
+            ble_l2cap_sm_gap_event(proc, status, 0);
+        }
+        ble_l2cap_sm_proc_free(proc);
     }
 }
 
 static int
-ble_l2cap_sm_proc_extract_cb(struct ble_fsm_proc *proc, void *arg)
+ble_l2cap_sm_proc_matches(struct ble_l2cap_sm_proc *proc, uint16_t conn_handle,
+                          uint8_t state, int is_initiator)
 {
-    struct ble_l2cap_sm_extract_arg *extarg;
-    struct ble_l2cap_sm_proc *sm_proc;
     int proc_is_initiator;
 
-    extarg = arg;
-    sm_proc = (struct ble_l2cap_sm_proc *)proc;
-
-    if (extarg->conn_handle != proc->conn_handle) {
-        return BLE_FSM_EXTRACT_EKEEP_CONTINUE;
+    if (conn_handle != proc->conn_handle) {
+        return 0;
     }
 
-    if (extarg->op != BLE_L2CAP_SM_PROC_OP_NONE && extarg->op != proc->op) {
-        return BLE_FSM_EXTRACT_EKEEP_CONTINUE;
+    if (state != BLE_L2CAP_SM_PROC_STATE_NONE && state != proc->state) {
+        return 0;
     }
 
-    proc_is_initiator = !!(sm_proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR);
-    if (extarg->initiator != -1 && extarg->initiator != proc_is_initiator) {
-        return BLE_FSM_EXTRACT_EKEEP_CONTINUE;
+    proc_is_initiator = !!(proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR);
+    if (is_initiator != -1 && is_initiator != proc_is_initiator) {
+        return 0;
     }
 
-    return BLE_FSM_EXTRACT_EMOVE_STOP;
+    return 1;
 }
 
 /**
- * Searches the main proc list for an "expecting" entry whose connection handle
- * and op code match those specified.  If a matching entry is found, it is
- * removed from the list and returned.
- *
- * Lock restrictions:
- *     o Caller unlocks l2cap_sm.
+ * Searches the main proc list for an entry whose connection handle and state code
+ * match those specified.  If a matching entry is found, it is removed from the
+ * list and returned.
  *
  * @param conn_handle           The connection handle to match against.
- * @param op                    The op code to match against.
- * @param identifier            The identifier to match against.
+ * @param state                    The state code to match against.
+ * @param is_initiator
  *
  * @return                      The matching proc entry on success;
  *                                  null on failure.
  */
 static struct ble_l2cap_sm_proc *
-ble_l2cap_sm_proc_extract(struct ble_l2cap_sm_extract_arg *arg)
+ble_l2cap_sm_proc_extract(uint16_t conn_handle, uint8_t state,
+                          int is_initiator)
 {
     struct ble_l2cap_sm_proc *proc;
-    int rc;
-
-    rc = ble_fsm_proc_extract(&ble_l2cap_sm_fsm,
-                              (struct ble_fsm_proc **)&proc,
-                              ble_l2cap_sm_proc_extract_cb, arg);
+    struct ble_l2cap_sm_proc *prev;
+
+    ble_hs_lock();
+
+    prev = NULL;
+    STAILQ_FOREACH(proc, &ble_l2cap_sm_procs, next) {
+        if (ble_l2cap_sm_proc_matches(proc, conn_handle, state, is_initiator)) {
+            if (prev == NULL) {
+                STAILQ_REMOVE_HEAD(&ble_l2cap_sm_procs, next);
+            } else {
+                STAILQ_REMOVE_AFTER(&ble_l2cap_sm_procs, prev, next);
+            }
+            break;
+        }
 
-    if (rc != 0) {
-        proc = NULL;
+        prev = proc;
     }
 
+    ble_hs_unlock();
+
     return proc;
 }
 
-/**
- * Sets the specified proc entry's "pending" flag (i.e., indicates that the
- * L2CAP sm procedure is stalled until it transmits its next request).
- *
- * Lock restrictions: None.
- */
 static void
-ble_l2cap_sm_proc_set_pending(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_extract_expired(struct ble_l2cap_sm_proc_list *dst_list)
 {
-    ble_fsm_proc_set_pending(&proc->fsm_proc);
-    ble_hs_kick_l2cap_sm();
-}
+    struct ble_l2cap_sm_proc *proc;
+    struct ble_l2cap_sm_proc *prev;
+    struct ble_l2cap_sm_proc *next;
+    uint32_t now;
+    int32_t time_diff;
 
-static void
-ble_l2cap_sm_set_fail_state(struct ble_l2cap_sm_proc *proc,
-                            uint8_t fail_reason)
-{
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_FAIL;
-    proc->fail.reason = fail_reason;
-    ble_l2cap_sm_proc_set_pending(proc);
+    now = os_time_get();
+    STAILQ_INIT(dst_list);
+
+    ble_hs_lock();
+
+    prev = NULL;
+    proc = STAILQ_FIRST(&ble_l2cap_sm_procs);
+    while (proc != NULL) {
+        next = STAILQ_NEXT(proc, next);
+
+        time_diff = now - proc->exp_os_ticks;
+        if (time_diff >= 0) {
+            if (prev == NULL) {
+                STAILQ_REMOVE_HEAD(&ble_l2cap_sm_procs, next);
+            } else {
+                STAILQ_REMOVE_AFTER(&ble_l2cap_sm_procs, prev, next);
+            }
+            STAILQ_INSERT_TAIL(dst_list, proc, next);
+        }
+
+        prev = proc;
+        proc = next;
+    }
+
+    ble_hs_unlock();
 }
 
-/**
- * Lock restrictions: None.
- */
 static int
-ble_l2cap_sm_rx_noop(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
+ble_l2cap_sm_rx_noop(uint16_t conn_handle, uint8_t state, struct os_mbuf **om)
 {
-    return 0;
+    return BLE_HS_ENOTSUP;
 }
 
+/*****************************************************************************
+ * $hci                                                                      *
+ *****************************************************************************/
+
 static int
-ble_l2cap_sm_request_tk(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_start_encrypt_tx(uint16_t conn_handle, uint8_t *ltk)
 {
-    if (proc->pair_alg == BLE_L2CAP_SM_PAIR_ALG_JW) {
-        ble_l2cap_sm_proc_set_pending(proc);
-    } else {
-        /* XXX: Ask application for TK. */
+    struct hci_start_encrypt cmd;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_START_ENCRYPT_LEN];
+    int rc;
+
+    rc = ble_l2cap_sm_gen_ediv(&cmd.encrypted_diversifier);
+    if (rc != 0) {
+        return rc;
     }
-    return 0;
-}
 
-static void
-ble_l2cap_sm_sec_params(struct ble_l2cap_sm_proc *proc,
-                        struct ble_gap_sec_params *out_sec_params,
-                        int enc_enabled)
-{
-    out_sec_params->pair_alg = proc->pair_alg;
-    out_sec_params->enc_enabled = enc_enabled;
-    out_sec_params->auth_enabled = 0; // XXX
-}
+    rc = ble_l2cap_sm_gen_start_rand(&cmd.random_number);
+    if (rc != 0) {
+        return rc;
+    }
 
-static void
-ble_l2cap_sm_gap_event(struct ble_l2cap_sm_proc *proc, int status,
-                       int enc_enabled)
-{
-    struct ble_gap_sec_params sec_params;
+    cmd.connection_handle = conn_handle;
+    memcpy(cmd.long_term_key, ltk, sizeof cmd.long_term_key);
 
-    ble_l2cap_sm_sec_params(proc, &sec_params, enc_enabled);
-    ble_gap_security_event(proc->fsm_proc.conn_handle, status, &sec_params);
-}
+    host_hci_cmd_build_le_start_encrypt(&cmd, buf, sizeof buf);
+    rc = ble_hci_tx_cmd_empty_ack(buf);
+    if (rc != 0) {
+        return rc;
+    }
 
-/*****************************************************************************
- * $pair                                                                     *
- *****************************************************************************/
+    return 0;
+}
 
 static int
-ble_l2cap_sm_pair_req_handle(struct ble_l2cap_sm_proc *proc,
-                             struct ble_l2cap_sm_pair_cmd *req)
+ble_l2cap_sm_lt_key_req_reply_tx(uint16_t conn_handle, uint8_t *ltk)
 {
-    proc->phase_1_2.pair_req = *req;
-    ble_l2cap_sm_proc_set_pending(proc);
+    struct hci_lt_key_req_reply cmd;
+    uint16_t ack_conn_handle;
+    uint8_t buf[BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_REPLY_LEN];
+    uint8_t ack_params_len;
+    int rc;
+
+    cmd.conn_handle = conn_handle;
+    memcpy(cmd.long_term_key, ltk, 16);
+
+    host_hci_cmd_build_le_lt_key_req_reply(&cmd, buf, sizeof buf);
+    rc = ble_hci_tx_cmd(buf, &ack_conn_handle, sizeof ack_conn_handle,
+                        &ack_params_len);
+    if (rc != 0) {
+        return rc;
+    }
+    if (ack_params_len != BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN - 1) {
+        return BLE_HS_ECONTROLLER;
+    }
+
+    ack_conn_handle = TOFROMLE16(ack_conn_handle);
+    if (ack_conn_handle != conn_handle) {
+        return BLE_HS_ECONTROLLER;
+    }
 
     return 0;
 }
 
 static int
-ble_l2cap_sm_pair_rsp_handle(struct ble_l2cap_sm_proc *proc,
-                             struct ble_l2cap_sm_pair_cmd *rsp)
+ble_l2cap_sm_lt_key_req_handle(struct ble_l2cap_sm_proc *proc,
+                               struct hci_le_lt_key_req *evt,
+                               uint8_t *out_sm_status)
 {
     int rc;
 
-    proc->phase_1_2.pair_rsp = *rsp;
+    rc = ble_l2cap_sm_lt_key_req_reply_tx(proc->conn_handle, proc->hci.key);
+    if (rc != 0) {
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
+    }
 
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_CONFIRM;
-    proc->fsm_proc.flags &= ~BLE_FSM_PROC_F_EXPECTING;
+    proc->state = BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE;
 
-    /* XXX: Assume legacy "Just Works" for now. */
-    proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
+    return 0;
+}
 
-    rc = ble_l2cap_sm_request_tk(proc);
+/*****************************************************************************
+ * $random                                                                   *
+ *****************************************************************************/
+
+static int
+ble_l2cap_sm_random_go(struct ble_l2cap_sm_proc *proc)
+{
+    struct ble_l2cap_sm_pair_random cmd;
+    int rc;
+
+    memcpy(cmd.value, proc->phase_1_2.rand_our, 16);
+    rc = ble_l2cap_sm_pair_random_tx(proc->conn_handle, &cmd);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, rc);
         return rc;
     }
 
+    ble_l2cap_sm_proc_set_timer(proc);
+
     return 0;
 }
 
 static int
-ble_l2cap_sm_pair_kick(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_random_handle(struct ble_l2cap_sm_proc *proc,
+                           struct ble_l2cap_sm_pair_random *cmd,
+                           uint8_t *out_sm_status)
 {
-    struct ble_l2cap_sm_pair_cmd cmd;
-    int is_req;
+    uint8_t preq[BLE_L2CAP_SM_HDR_SZ + BLE_L2CAP_SM_PAIR_CMD_SZ];
+    uint8_t pres[BLE_L2CAP_SM_HDR_SZ + BLE_L2CAP_SM_PAIR_CMD_SZ];
+    uint8_t confirm_val[16];
+    uint8_t k[16];
+    uint8_t ia[6];
+    uint8_t ra[6];
+    uint8_t iat;
+    uint8_t rat;
     int rc;
 
-    is_req = proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR;
+    /* Verify peer's random value. */
+    rc = ble_l2cap_sm_confirm_prepare_args(proc, k, preq, pres, &iat, &rat,
+                                           ia, ra);
+    if (rc != 0) {
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
+    }
 
-    cmd.io_cap = ble_hs_cfg.sm_io_cap;
-    cmd.oob_data_flag = ble_hs_cfg.sm_oob_data_flag;
-    cmd.authreq = (ble_hs_cfg.sm_bonding << 0)  |
-                  (ble_hs_cfg.sm_mitm << 2)     |
-                  (ble_hs_cfg.sm_sc << 3)       |
-                  (ble_hs_cfg.sm_keypress << 4);
-    cmd.max_enc_key_size = 16;
+    rc = ble_l2cap_sm_alg_c1(k, cmd->value, preq, pres, iat, rat,
+                             ia, ra, confirm_val);
+    if (rc != 0) {
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
+    }
 
-    if (is_req) {
-        cmd.init_key_dist = ble_hs_cfg.sm_our_key_dist;
-        cmd.resp_key_dist = ble_hs_cfg.sm_their_key_dist;
-    } else {
-        cmd.init_key_dist = ble_hs_cfg.sm_their_key_dist;
-        cmd.resp_key_dist = ble_hs_cfg.sm_our_key_dist;
+    if (memcmp(proc->phase_1_2.confirm_their, confirm_val, 16) != 0) {
+        /* Random number mismatch. */
+        rc = BLE_HS_SM_US_ERR(BLE_L2CAP_SM_ERR_CONFIRM_MISMATCH);
+        *out_sm_status = BLE_L2CAP_SM_ERR_CONFIRM_MISMATCH;
+        return rc;
     }
 
-    rc = ble_l2cap_sm_pair_cmd_tx(proc->fsm_proc.conn_handle, is_req, &cmd);
+    memcpy(proc->phase_1_2.rand_their, cmd->value, 16);
+
+    /* Generate the key. */
+    rc = ble_l2cap_sm_gen_key(proc);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return BLE_HS_EAGAIN;
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
     }
 
-    if (is_req) {
-        proc->phase_1_2.pair_req = cmd;
+    if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
+        /* Send the start-encrypt HCI command to the controller. */
+        rc = ble_l2cap_sm_start_encrypt_tx(proc->conn_handle, proc->hci.key);
+        if (rc != 0) {
+            *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+        proc->state = BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE;
     } else {
-        proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
-        proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_CONFIRM;
-        proc->phase_1_2.pair_rsp = cmd;
+        rc = ble_l2cap_sm_random_go(proc);
+        if (rc != 0) {
+            *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
+        proc->state = BLE_L2CAP_SM_PROC_STATE_LTK;
     }
 
+    *out_sm_status = 0;
     return 0;
 }
 
@@ -605,8 +633,7 @@ ble_l2cap_sm_confirm_prepare_args(struct ble_l2cap_sm_proc *proc,
 {
     struct ble_hs_conn *conn;
 
-    ble_hs_conn_lock();
-    conn = ble_hs_conn_find(proc->fsm_proc.conn_handle);
+    conn = ble_hs_conn_find(proc->conn_handle);
     if (conn != NULL) {
         if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
             *iat = BLE_ADDR_TYPE_PUBLIC; /* XXX: Support random addresses. */
@@ -622,7 +649,6 @@ ble_l2cap_sm_confirm_prepare_args(struct ble_l2cap_sm_proc *proc,
             memcpy(ia, conn->bhc_addr, 6);
         }
     }
-    ble_hs_conn_unlock();
 
     if (conn == NULL) {
         return BLE_HS_ENOTCONN;
@@ -642,7 +668,7 @@ ble_l2cap_sm_confirm_prepare_args(struct ble_l2cap_sm_proc *proc,
 }
 
 static int
-ble_l2cap_sm_confirm_kick(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_confirm_go(struct ble_l2cap_sm_proc *proc)
 {
     struct ble_l2cap_sm_pair_confirm cmd;
     uint8_t preq[BLE_L2CAP_SM_HDR_SZ + BLE_L2CAP_SM_PAIR_CMD_SZ];
@@ -654,271 +680,150 @@ ble_l2cap_sm_confirm_kick(struct ble_l2cap_sm_proc *proc)
     uint8_t rat;
     int rc;
 
-    ble_l2cap_sm_gen_pair_rand(proc->phase_1_2.rand_our);
+    rc = ble_l2cap_sm_gen_pair_rand(proc->phase_1_2.rand_our);
+    if (rc != 0) {
+        return rc;
+    }
 
     rc = ble_l2cap_sm_confirm_prepare_args(proc, k, preq, pres, &iat, &rat,
                                            ia, ra);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return BLE_HS_EAGAIN;
+        return rc;
     }
 
     rc = ble_l2cap_sm_alg_c1(k, proc->phase_1_2.rand_our, preq, pres, iat, rat,
                              ia, ra, cmd.value);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return BLE_HS_EAGAIN;
+        return rc;
     }
 
-    rc = ble_l2cap_sm_pair_confirm_tx(proc->fsm_proc.conn_handle, &cmd);
+    rc = ble_l2cap_sm_pair_confirm_tx(proc->conn_handle, &cmd);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return BLE_HS_EAGAIN;
+        return rc;
     }
 
-    if (!(proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR)) {
-        proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_RANDOM;
-    }
+    ble_l2cap_sm_proc_set_timer(proc);
 
     return 0;
 }
 
 static int
 ble_l2cap_sm_confirm_handle(struct ble_l2cap_sm_proc *proc,
-                            struct ble_l2cap_sm_pair_confirm *cmd)
+                            struct ble_l2cap_sm_pair_confirm *cmd,
+                            uint8_t *out_sm_status)
 {
     int rc;
 
     memcpy(proc->phase_1_2.confirm_their, cmd->value, 16);
 
     if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
-        proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_RANDOM;
-        ble_l2cap_sm_proc_set_pending(proc);
+        rc = ble_l2cap_sm_random_go(proc);
+        if (rc != 0) {
+            *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+            return rc;
+        }
     } else {
-        rc = ble_l2cap_sm_request_tk(proc);
+        /* XXX: If MITM is used, request TK from application. */
+
+        rc = ble_l2cap_sm_confirm_go(proc);
         if (rc != 0) {
-            ble_l2cap_sm_set_fail_state(proc, rc);
+            *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
             return rc;
         }
     }
 
+    proc->state = BLE_L2CAP_SM_PROC_STATE_RANDOM;
+
     return 0;
 }
 
 /*****************************************************************************
- * $random                                                                  *
+ * $pair                                                                     *
  *****************************************************************************/
 
 static int
-ble_l2cap_sm_random_kick(struct ble_l2cap_sm_proc *proc)
+ble_l2cap_sm_pair_go(struct ble_l2cap_sm_proc *proc)
 {
-    struct ble_l2cap_sm_pair_random cmd;
-    uint8_t key[16];
+    struct ble_l2cap_sm_pair_cmd cmd;
+    int is_req;
     int rc;
 
-    memcpy(cmd.value, proc->phase_1_2.rand_our, 16);
-    rc = ble_l2cap_sm_pair_random_tx(proc->fsm_proc.conn_handle, &cmd);
-    if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return BLE_HS_EAGAIN;
-    }
-
-    if (!(proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR)) {
-        /* Generate the key. */
-        rc = ble_l2cap_sm_alg_s1(proc->phase_1_2.tk, proc->phase_1_2.rand_our,
-                                 proc->phase_1_2.rand_their, key);
-        if (rc != 0) {
-            ble_l2cap_sm_gap_event(proc, rc, 0);
-            return rc;
-        }
-        memcpy(proc->hci.key, key, sizeof key);
-
-        proc->hci.handle = BLE_HCI_SCHED_HANDLE_NONE;
-        proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_LTK;
-    }
-
-    return 0;
-}
+    is_req = proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR;
 
-static int
-ble_l2cap_sm_random_handle(struct ble_l2cap_sm_proc *proc,
-                           struct ble_l2cap_sm_pair_random *cmd)
-{
-    uint8_t preq[BLE_L2CAP_SM_HDR_SZ + BLE_L2CAP_SM_PAIR_CMD_SZ];
-    uint8_t pres[BLE_L2CAP_SM_HDR_SZ + BLE_L2CAP_SM_PAIR_CMD_SZ];
-    uint8_t confirm_val[16];
-    uint8_t key[16];
-    uint8_t k[16];
-    uint8_t ia[6];
-    uint8_t ra[6];
-    uint8_t iat;
-    uint8_t rat;
-    int rc;
+    cmd.io_cap = ble_hs_cfg.sm_io_cap;
+    cmd.oob_data_flag = ble_hs_cfg.sm_oob_data_flag;
+    cmd.authreq = (ble_hs_cfg.sm_bonding << 0)  |
+                  (ble_hs_cfg.sm_mitm << 2)     |
+                  (ble_hs_cfg.sm_sc << 3)       |
+                  (ble_hs_cfg.sm_keypress << 4);
+    cmd.max_enc_key_size = 16;
 
-    /* Verify peer's random value. */
-    rc = ble_l2cap_sm_confirm_prepare_args(proc, k, preq, pres, &iat, &rat,
-                                           ia, ra);
-    if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return 0;
+    if (is_req) {
+        cmd.init_key_dist = ble_hs_cfg.sm_our_key_dist;
+        cmd.resp_key_dist = ble_hs_cfg.sm_their_key_dist;
+    } else {
+        cmd.init_key_dist = ble_hs_cfg.sm_their_key_dist;
+        cmd.resp_key_dist = ble_hs_cfg.sm_our_key_dist;
     }
 
-    rc = ble_l2cap_sm_alg_c1(k, cmd->value, preq, pres, iat, rat,
-                             ia, ra, confirm_val);
+    rc = ble_l2cap_sm_pair_cmd_tx(proc->conn_handle, is_req, &cmd);
     if (rc != 0) {
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_UNSPECIFIED);
-        return 0;
+        return rc;
     }
 
-    if (memcmp(proc->phase_1_2.confirm_their, confirm_val, 16) != 0) {
-        /* Random number mismatch. */
-        ble_l2cap_sm_set_fail_state(proc, BLE_L2CAP_SM_ERR_CONFIRM_MISMATCH);
-        return 0;
+    if (is_req) {
+        proc->phase_1_2.pair_req = cmd;
+    } else {
+        proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
+        proc->phase_1_2.pair_rsp = cmd;
     }
 
-    memcpy(proc->phase_1_2.rand_their, cmd->value, 16);
-
-    if (proc->flags & BLE_L2CAP_SM_PROC_F_INITIATOR) {
-        /* Generate the key. */
-        rc = ble_l2cap_sm_alg_s1(proc->phase_1_2.tk, proc->phase_1_2.rand_our,
-                                 proc->phase_1_2.rand_their, key);
-        if (rc != 0) {
-            ble_l2cap_sm_gap_event(proc, rc, 0);
-            return rc;
-        }
-        memcpy(proc->hci.key, key, sizeof key);
-        proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_START_ENCRYPT;
-    }
-    ble_l2cap_sm_proc_set_pending(proc);
+    ble_l2cap_sm_proc_set_timer(proc);
 
     return 0;
 }
 
-/*****************************************************************************
- * $fail                                                                    *
- *****************************************************************************/
-
-static int
-ble_l2cap_sm_fail_kick(struct ble_l2cap_sm_proc *proc)
-{
-    struct ble_l2cap_sm_pair_fail cmd;
-
-    cmd.reason = proc->fail.reason;
-    ble_l2cap_sm_pair_fail_tx(proc->fsm_proc.conn_handle, &cmd);
-
-    /* Notify application of failure. */
-    ble_l2cap_sm_gap_event(proc, BLE_HS_SM_US_ERR(cmd.reason), 0);
-
-    return BLE_HS_EDONE;
-}
-
 static int
-ble_l2cap_sm_fail_handle(struct ble_l2cap_sm_proc *proc,
-                         struct ble_l2cap_sm_pair_fail *cmd)
-{
-    ble_l2cap_sm_gap_event(proc, BLE_HS_SM_THEM_ERR(cmd->reason), 0);
-
-    /* Procedure should now be terminated (return nonzero). */
-    return 1;
-}
-
-/*****************************************************************************
- * $hci                                                                      *
- *****************************************************************************/
-
-static int
-ble_l2cap_sm_lt_key_req_reply_tx(void *arg)
+ble_l2cap_sm_pair_req_handle(struct ble_l2cap_sm_proc *proc,
+                             struct ble_l2cap_sm_pair_cmd *req,
+                             uint8_t *out_sm_status)
 {
-    struct hci_lt_key_req_reply cmd;
-    struct ble_l2cap_sm_proc *proc;
     int rc;
 
-    proc = arg;
-
-    BLE_HS_DBG_ASSERT(proc->fsm_proc.op == BLE_L2CAP_SM_PROC_OP_LTK_TXED);
-
-    /* Indicate that the HCI reservation has been serviced.  If there is a
-     * failure, we shouldn't try to cancel the reservation.
-     */
-    proc->hci.handle = BLE_HCI_SCHED_HANDLE_NONE;
-
-    cmd.conn_handle = proc->fsm_proc.conn_handle;
-    memcpy(cmd.long_term_key, proc->hci.key, 16);
-
-    ble_hci_sched_set_ack_cb(ble_l2cap_sm_rx_lt_key_req_reply_ack, proc);
-
-    rc = host_hci_cmd_le_lt_key_req_reply(&cmd);
-    return rc;
-}
-
-static int
-ble_l2cap_sm_lt_key_req_kick(struct ble_l2cap_sm_proc *proc)
-{
-    int rc;
+    proc->phase_1_2.pair_req = *req;
 
-    rc = ble_hci_sched_enqueue(ble_l2cap_sm_lt_key_req_reply_tx, proc,
-                               &proc->hci.handle);
+    rc = ble_l2cap_sm_pair_go(proc);
     if (rc != 0) {
-        ble_l2cap_sm_gap_event(proc, rc, 0);
-        return BLE_HS_EDONE;
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
     }
 
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_LTK_TXED;
+    proc->state = BLE_L2CAP_SM_PROC_STATE_CONFIRM;
 
     return 0;
 }
 
 static int
-ble_l2cap_sm_lt_key_req_handle(struct ble_l2cap_sm_proc *proc,
-                               struct hci_le_lt_key_req *evt)
-{
-    ble_l2cap_sm_proc_set_pending(proc);
-    return 0;
-}
-
-static int
-ble_l2cap_sm_start_encrypt_tx(void *arg)
+ble_l2cap_sm_pair_rsp_handle(struct ble_l2cap_sm_proc *proc,
+                             struct ble_l2cap_sm_pair_cmd *rsp,
+                             uint8_t *out_sm_status)
 {
-    struct ble_l2cap_sm_proc *proc;
-    struct hci_start_encrypt cmd;
     int rc;
 
-    proc = arg;
-
-    BLE_HS_DBG_ASSERT(proc->fsm_proc.op ==
-                      BLE_L2CAP_SM_PROC_OP_START_ENCRYPT_TXED);
-
-    /* Indicate that the HCI reservation has been serviced.  If there is a
-     * failure, we shouldn't try to cancel the reservation.
-     */
-    proc->hci.handle = BLE_HCI_SCHED_HANDLE_NONE;
-
-    cmd.connection_handle = proc->fsm_proc.conn_handle;
-    cmd.encrypted_diversifier = ble_l2cap_sm_gen_ediv();
-    cmd.random_number = ble_l2cap_sm_gen_start_rand();
-    memcpy(cmd.long_term_key, proc->hci.key, sizeof cmd.long_term_key);
-
-    ble_hci_sched_set_ack_cb(ble_l2cap_sm_rx_start_encrypt_ack, proc);
+    proc->phase_1_2.pair_rsp = *rsp;
 
-    rc = host_hci_cmd_le_start_encrypt(&cmd);
-    return rc;
-}
+    /* XXX: Assume legacy "Just Works" for now. */
+    proc->pair_alg = BLE_L2CAP_SM_PAIR_ALG_JW;
 
-static int
-ble_l2cap_sm_start_encrypt_kick(struct ble_l2cap_sm_proc *proc)
-{
-    int rc;
+    /* XXX: If MITM is used, request TK from application. */
 
-    rc = ble_hci_sched_enqueue(ble_l2cap_sm_start_encrypt_tx, proc,
-                               &proc->hci.handle);
+    proc->state = BLE_L2CAP_SM_PROC_STATE_CONFIRM;
+    rc = ble_l2cap_sm_confirm_go(proc);
     if (rc != 0) {
-        ble_l2cap_sm_gap_event(proc, rc, 0);
-        return BLE_HS_EDONE;
+        *out_sm_status = BLE_L2CAP_SM_ERR_UNSPECIFIED;
+        return rc;
     }
 
-    proc->hci.handle = BLE_HCI_SCHED_HANDLE_NONE;
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_START_ENCRYPT_TXED;
-
     return 0;
 }
 
@@ -927,10 +832,12 @@ ble_l2cap_sm_start_encrypt_kick(struct ble_l2cap_sm_proc *proc)
  *****************************************************************************/
 
 static int
-ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
+ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t state,
+                         struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_cmd req;
     struct ble_l2cap_sm_proc *proc;
+    uint8_t sm_status;
     int rc;
 
     rc = ble_hs_misc_pullup_base(om, BLE_L2CAP_SM_PAIR_CMD_SZ);
@@ -948,36 +855,34 @@ ble_l2cap_sm_rx_pair_req(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
 
     /* XXX: Check connection state; reject if not appropriate. */
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_NONE,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle, BLE_L2CAP_SM_PROC_STATE_NONE,
+                                     -1);
     if (proc != NULL) {
         /* Pairing already in progress; abort old procedure and start new. */
         /* XXX: Check the spec on this. */
-        ble_l2cap_sm_proc_free(&proc->fsm_proc);
+        ble_l2cap_sm_proc_free(proc);
     }
 
-    rc = ble_l2cap_sm_proc_new(conn_handle, BLE_L2CAP_SM_PROC_OP_PAIR,
-                               &proc);
-    if (rc != 0) {
-        return rc;
+    proc = ble_l2cap_sm_proc_alloc();
+    if (proc == NULL) {
+        return BLE_HS_ENOMEM;
     }
+    proc->conn_handle = conn_handle;
+    proc->state = BLE_L2CAP_SM_PROC_STATE_PAIR;
 
-    rc = ble_l2cap_sm_pair_req_handle(proc, &req);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    rc = ble_l2cap_sm_pair_req_handle(proc, &req, &sm_status);
+    ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
 
     return rc;
 }
 
 static int
-ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
+ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t state,
+                         struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_cmd rsp;
     struct ble_l2cap_sm_proc *proc;
+    uint8_t sm_status;
     int rc;
 
     rc = ble_hs_misc_pullup_base(om, BLE_L2CAP_SM_PAIR_CMD_SZ);
@@ -993,29 +898,25 @@ ble_l2cap_sm_rx_pair_rsp(uint16_t conn_handle, uint8_t op, struct os_mbuf **om)
                rsp.io_cap, rsp.oob_data_flag, rsp.authreq,
                rsp.max_enc_key_size, rsp.init_key_dist, rsp.resp_key_dist);
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_PAIR,
-            .initiator = 1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle, BLE_L2CAP_SM_PROC_STATE_PAIR,
+                                     1);
     if (proc == NULL) {
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_l2cap_sm_pair_rsp_handle(proc, &rsp);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    rc = ble_l2cap_sm_pair_rsp_handle(proc, &rsp, &sm_status);
+    ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
 
     return 0;
 }
 
 static int
-ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t op,
+ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t state,
                              struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_confirm cmd;
     struct ble_l2cap_sm_proc *proc;
+    uint8_t sm_status;
     int rc;
 
     rc = ble_hs_misc_pullup_base(om, BLE_L2CAP_SM_PAIR_CONFIRM_SZ);
@@ -1027,29 +928,25 @@ ble_l2cap_sm_rx_pair_confirm(uint16_t conn_handle, uint8_t op,
 
     BLE_HS_LOG(DEBUG, "rxed sm confirm cmd\n");
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_CONFIRM,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle,
+                                     BLE_L2CAP_SM_PROC_STATE_CONFIRM, -1);
     if (proc == NULL) {
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_l2cap_sm_confirm_handle(proc, &cmd);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    rc = ble_l2cap_sm_confirm_handle(proc, &cmd, &sm_status);
+    ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
 
     return 0;
 }
 
 static int
-ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t op,
+ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t state,
                             struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_random cmd;
     struct ble_l2cap_sm_proc *proc;
+    uint8_t sm_status;
     int rc;
 
     rc = ble_hs_misc_pullup_base(om, BLE_L2CAP_SM_PAIR_RANDOM_SZ);
@@ -1061,25 +958,20 @@ ble_l2cap_sm_rx_pair_random(uint16_t conn_handle, uint8_t op,
 
     BLE_HS_LOG(DEBUG, "rxed sm random cmd\n");
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_RANDOM,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle,
+                                     BLE_L2CAP_SM_PROC_STATE_RANDOM, -1);
     if (proc == NULL) {
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_l2cap_sm_random_handle(proc, &cmd);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    rc = ble_l2cap_sm_random_handle(proc, &cmd, &sm_status);
+    ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
 
     return 0;
 }
 
 static int
-ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t op,
+ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t state,
                           struct os_mbuf **om)
 {
     struct ble_l2cap_sm_pair_fail cmd;
@@ -1095,19 +987,13 @@ ble_l2cap_sm_rx_pair_fail(uint16_t conn_handle, uint8_t op,
 
     BLE_HS_LOG(DEBUG, "rxed sm fail cmd; reason=%d\n", cmd.reason);
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_NONE,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle, BLE_L2CAP_SM_PROC_STATE_NONE,
+                                     -1);
     if (proc == NULL) {
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_l2cap_sm_fail_handle(proc, &cmd);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    ble_l2cap_sm_process_status(proc, BLE_HS_SM_THEM_ERR(cmd.reason), 0, 1, 0);
 
     return 0;
 }
@@ -1116,129 +1002,38 @@ int
 ble_l2cap_sm_rx_lt_key_req(struct hci_le_lt_key_req *evt)
 {
     struct ble_l2cap_sm_proc *proc;
+    uint8_t sm_status;
     int rc;
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = evt->connection_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_LTK,
-            .initiator = 0,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(evt->connection_handle,
+                                     BLE_L2CAP_SM_PROC_STATE_LTK, 0);
     if (proc == NULL) {
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_l2cap_sm_lt_key_req_handle(proc, evt);
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
+    rc = ble_l2cap_sm_lt_key_req_handle(proc, evt, &sm_status);
+    ble_l2cap_sm_process_status(proc, rc, sm_status, 1, 1);
 
     return 0;
 }
 
-static void
-ble_l2cap_sm_rx_lt_key_req_reply_ack(struct ble_hci_ack *ack, void *arg)
-{
-    struct ble_l2cap_sm_proc *proc;
-    uint16_t conn_handle;
-    int rc;
-
-    proc = arg;
-
-    BLE_HS_DBG_ASSERT(proc->fsm_proc.op == BLE_L2CAP_SM_PROC_OP_LTK_TXED);
-
-    /* Extract the procedure from the state machine while we mess with it. */
-    ble_fsm_lock(&ble_l2cap_sm_fsm);
-    STAILQ_REMOVE(&ble_l2cap_sm_fsm.procs, &proc->fsm_proc, ble_fsm_proc,
-                  next);
-    ble_fsm_unlock(&ble_l2cap_sm_fsm);
-
-    if (ack->bha_status != 0) {
-        rc = ack->bha_status;
-        goto done;
-    }
-
-    if (ack->bha_params_len != BLE_HCI_LT_KEY_REQ_REPLY_ACK_PARAM_LEN) {
-        /* Controller sent something weird; treat this as a failure. */
-        rc = BLE_HS_ECONTROLLER;
-        goto done;
-    }
-
-    conn_handle = le16toh(ack->bha_params + 1);
-    if (conn_handle != proc->fsm_proc.conn_handle) {
-        /* Controller sent something weird; treat this as a failure. */
-        rc = BLE_HS_ECONTROLLER;
-        goto done;
-    }
-
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_ENC_CHANGE;
-    rc = 0;
-
-done:
-    if (rc != 0) {
-        /* Report the failure to the application. */
-        ble_l2cap_sm_gap_event(proc, rc, 0);
-    }
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
-}
-
-static void
-ble_l2cap_sm_rx_start_encrypt_ack(struct ble_hci_ack *ack, void *arg)
-{
-    struct ble_l2cap_sm_proc *proc;
-    int rc;
-
-    proc = arg;
-
-    BLE_HS_DBG_ASSERT(proc->fsm_proc.op ==
-                      BLE_L2CAP_SM_PROC_OP_START_ENCRYPT_TXED);
-
-    /* Extract the procedure from the state machine while we mess with it. */
-    ble_fsm_lock(&ble_l2cap_sm_fsm);
-    STAILQ_REMOVE(&ble_l2cap_sm_fsm.procs, &proc->fsm_proc, ble_fsm_proc,
-                  next);
-    ble_fsm_unlock(&ble_l2cap_sm_fsm);
-
-    if (ack->bha_status != 0) {
-        rc = ack->bha_status;
-        goto done;
-    }
-
-    proc->fsm_proc.op = BLE_L2CAP_SM_PROC_OP_ENC_CHANGE;
-    rc = 0;
-
-done:
-    if (rc != 0) {
-        /* Report the failure to the application. */
-        ble_l2cap_sm_gap_event(proc, rc, 0);
-    }
-    ble_fsm_process_rx_status(&ble_l2cap_sm_fsm, &proc->fsm_proc, rc);
-}
-
 void
 ble_l2cap_sm_rx_encryption_change(struct hci_encrypt_change *evt)
 {
     struct ble_l2cap_sm_proc *proc;
+    int enc_enabled;
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = evt->connection_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_ENC_CHANGE,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(evt->connection_handle,
+                                     BLE_L2CAP_SM_PROC_STATE_ENC_CHANGE, -1);
     if (proc == NULL) {
         return;
     }
 
-    if (evt->status != 0) {
-        ble_l2cap_sm_gap_event(proc, BLE_HS_HCI_ERR(evt->status), 0);
-    } else {
-        ble_l2cap_sm_gap_event(proc, 0,
-                               evt->encryption_enabled & 0x01 /* LE bit. */);
-    }
+    enc_enabled = evt->encryption_enabled & 0x01; /* LE bit. */
+    ble_l2cap_sm_gap_event(proc, BLE_HS_HCI_ERR(evt->status), enc_enabled);
 
     /* The pairing procedure is now complete. */
-    ble_l2cap_sm_proc_free(&proc->fsm_proc);
+    ble_l2cap_sm_proc_free(proc);
 }
 
 /**
@@ -1257,11 +1052,10 @@ ble_l2cap_sm_rx(uint16_t conn_handle, struct os_mbuf **om)
     ble_hs_misc_log_mbuf(*om);
     BLE_HS_LOG(DEBUG, "\n");
 
-    rc = ble_hs_misc_pullup_base(om, 1);
+    rc = os_mbuf_copydata(*om, 0, 1, &op);
     if (rc != 0) {
         return BLE_HS_EBADDATA;
     }
-    op = *(*om)->om_data;
 
     /* Strip L2CAP SM header from the front of the mbuf. */
     os_mbuf_adj(*om, 1);
@@ -1283,25 +1077,22 @@ ble_l2cap_sm_rx(uint16_t conn_handle, struct os_mbuf **om)
 void
 ble_l2cap_sm_heartbeat(void)
 {
-    struct ble_fsm_proc_list exp_list;
+    struct ble_l2cap_sm_proc_list exp_list;
     struct ble_l2cap_sm_proc *proc;
-    struct ble_fsm_proc *fsm_proc;
 
     /* Remove all timed out procedures and insert them into a temporary
      * list.
      */
-    ble_fsm_proc_extract_list(&ble_l2cap_sm_fsm, &exp_list,
-                              ble_l2cap_sm_proc_extract_expired_cb, NULL);
+    ble_l2cap_sm_extract_expired(&exp_list);
 
     /* Notify application of each failure and free the corresponding procedure
      * object.
      */
-    while ((fsm_proc = STAILQ_FIRST(&exp_list)) != NULL) {
-        proc = (struct ble_l2cap_sm_proc *)fsm_proc;
+    while ((proc = STAILQ_FIRST(&exp_list)) != NULL) {
         ble_l2cap_sm_gap_event(proc, BLE_HS_ETIMEOUT, 0);
 
         STAILQ_REMOVE_HEAD(&exp_list, next);
-        ble_l2cap_sm_proc_free(&proc->fsm_proc);
+        ble_l2cap_sm_proc_free(proc);
     }
 }
 
@@ -1314,28 +1105,24 @@ ble_l2cap_sm_initiate(uint16_t conn_handle)
     /* Make sure a pairing operation for this connection is not already in
      * progress.
      */
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_NONE,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle, BLE_L2CAP_SM_PROC_STATE_NONE,
+                                     -1);
     if (proc != NULL) {
         return BLE_HS_EALREADY;
     }
 
-    rc = ble_l2cap_sm_proc_new(conn_handle, BLE_L2CAP_SM_PROC_OP_PAIR,
-                               &proc);
-    if (rc != 0) {
-        return rc;
+    proc = ble_l2cap_sm_proc_alloc();
+    if (proc == NULL) {
+        return BLE_HS_ENOMEM;
     }
-
+    proc->conn_handle = conn_handle;
+    proc->state = BLE_L2CAP_SM_PROC_STATE_PAIR;
     proc->flags |= BLE_L2CAP_SM_PROC_F_INITIATOR;
-    STAILQ_INSERT_TAIL(&ble_l2cap_sm_fsm.procs, &proc->fsm_proc, next);
-    ble_l2cap_sm_proc_set_pending(proc);
 
-    return 0;
+    rc = ble_l2cap_sm_pair_go(proc);
+    ble_l2cap_sm_process_status(proc, rc, 0, 0, 0);
+
+    return rc;
 }
 
 /**
@@ -1364,20 +1151,15 @@ ble_l2cap_sm_set_tk(uint16_t conn_handle, uint8_t *tk)
 {
     struct ble_l2cap_sm_proc *proc;
 
-    proc = ble_l2cap_sm_proc_extract(
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_CONFIRM,
-            .initiator = -1,
-        }
-    );
+    proc = ble_l2cap_sm_proc_extract(conn_handle,
+                                     BLE_L2CAP_SM_PROC_STATE_CONFIRM, -1);
     if (proc == NULL) {
         return BLE_HS_ENOENT;
     }
 
     memcpy(proc->phase_1_2.tk, tk, 16);
 
-    ble_l2cap_sm_proc_set_pending(proc);
+    /* XXX: Proceed with pairing; send confirm command. */
 
     return 0;
 }
@@ -1385,43 +1167,19 @@ ble_l2cap_sm_set_tk(uint16_t conn_handle, uint8_t *tk)
 void
 ble_l2cap_sm_connection_broken(uint16_t conn_handle)
 {
-    struct ble_fsm_proc_list list;
     struct ble_l2cap_sm_proc *proc;
-    struct ble_fsm_proc *fsm_proc;
-
-    /* Extract all procs associated with the broken connection and insert them
-     * into the temporary list.
-     */
-    ble_fsm_proc_extract_list(
-        &ble_l2cap_sm_fsm, &list, ble_l2cap_sm_proc_extract_cb,
-        &(struct ble_l2cap_sm_extract_arg) {
-            .conn_handle = conn_handle,
-            .op = BLE_L2CAP_SM_PROC_OP_NONE,
-            .initiator = -1,
-        }
-    );
-
-    /* Free each affected procedure object.  There is no need to notify the
-     * application, as it has already been notified of the connection failure.
-     */
-    while ((fsm_proc = STAILQ_FIRST(&list)) != NULL) {
-        proc = (struct ble_l2cap_sm_proc *)fsm_proc;
 
-        STAILQ_REMOVE_HEAD(&list, next);
-        ble_l2cap_sm_proc_free(&proc->fsm_proc);
+    proc = ble_l2cap_sm_proc_extract(conn_handle, BLE_L2CAP_SM_PROC_STATE_NONE,
+                                     -1);
+    if (proc != NULL) {
+        /* Free thw affected procedure object.  There is no need to notify the
+         * application, as it has already been notified of the connection
+         * failure.
+         */
+        ble_l2cap_sm_proc_free(proc);
     }
 }
 
-/**
- * Lock restrictions:
- *     o Caller unlocks ble_hs_conn.
- */
-void
-ble_l2cap_sm_wakeup(void)
-{
-    ble_fsm_wakeup(&ble_l2cap_sm_fsm);
-}
-
 int
 ble_l2cap_sm_init(void)
 {
@@ -1429,11 +1187,7 @@ ble_l2cap_sm_init(void)
 
     free(ble_l2cap_sm_proc_mem);
 
-    rc = ble_fsm_new(&ble_l2cap_sm_fsm, ble_l2cap_sm_proc_kick,
-                     ble_l2cap_sm_proc_free);
-    if (rc != 0) {
-        goto err;
-    }
+    STAILQ_INIT(&ble_l2cap_sm_procs);
 
     if (ble_hs_cfg.max_l2cap_sm_procs > 0) {
         ble_l2cap_sm_proc_mem = malloc(

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e32f9f9f/net/nimble/host/src/ble_l2cap_sm.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm.h b/net/nimble/host/src/ble_l2cap_sm.h
index e56f1df..9ecbeaf 100644
--- a/net/nimble/host/src/ble_l2cap_sm.h
+++ b/net/nimble/host/src/ble_l2cap_sm.h
@@ -90,8 +90,6 @@ void ble_l2cap_sm_dbg_set_next_start_rand(uint64_t next_start_rand);
 int ble_l2cap_sm_dbg_num_procs(void);
 #endif
 
-int ble_l2cap_sm_locked_by_cur_task(void);
-
 struct ble_l2cap_chan *ble_l2cap_sm_create_chan(void);
 
 void ble_l2cap_sm_pair_cmd_parse(void *payload, int len,
@@ -116,8 +114,7 @@ void ble_l2cap_sm_pair_fail_parse(void *payload, int len,
                                   struct ble_l2cap_sm_pair_fail *cmd);
 void ble_l2cap_sm_pair_fail_write(void *payload, int len,
                                   struct ble_l2cap_sm_pair_fail *cmd);
-int ble_l2cap_sm_pair_fail_tx(uint16_t conn_handle,
-                              struct ble_l2cap_sm_pair_fail *cmd);
+int ble_l2cap_sm_pair_fail_tx(uint16_t conn_handle, uint8_t reason);
 
 int ble_l2cap_sm_alg_s1(uint8_t *k, uint8_t *r1, uint8_t *r2, uint8_t *out);
 int ble_l2cap_sm_alg_c1(uint8_t *k, uint8_t *r,
@@ -131,7 +128,6 @@ int ble_l2cap_sm_rx_lt_key_req(struct hci_le_lt_key_req *evt);
 
 void ble_l2cap_sm_heartbeat(void);
 int ble_l2cap_sm_initiate(uint16_t conn_handle);
-void ble_l2cap_sm_wakeup(void);
 int ble_l2cap_sm_init(void);
 
 #else
@@ -141,8 +137,6 @@ int ble_l2cap_sm_init(void);
 #define ble_l2cap_sm_dbg_num_procs() 0
 #endif
 
-#define ble_l2cap_sm_locked_by_cur_task() 0
-
 #define ble_l2cap_sm_create_chan() NULL
 
 #define ble_l2cap_sm_pair_cmd_tx(conn_handle, is_req, cmd) BLE_HS_ENOTSUP
@@ -160,7 +154,6 @@ int ble_l2cap_sm_init(void);
 #define ble_l2cap_sm_connection_broken(conn_handle)
 
 #define ble_l2cap_sm_heartbeat()
-#define ble_l2cap_sm_wakeup()
 #define ble_l2cap_sm_init() 0
 
 #endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e32f9f9f/net/nimble/host/src/ble_l2cap_sm_cmd.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_l2cap_sm_cmd.c b/net/nimble/host/src/ble_l2cap_sm_cmd.c
index dac8558..843b1e5 100644
--- a/net/nimble/host/src/ble_l2cap_sm_cmd.c
+++ b/net/nimble/host/src/ble_l2cap_sm_cmd.c
@@ -35,7 +35,7 @@ ble_l2cap_sm_tx(uint16_t conn_handle, struct os_mbuf *txom)
 
     STATS_INC(ble_l2cap_stats, sm_tx);
 
-    ble_hs_conn_lock();
+    ble_hs_lock();
 
     rc = ble_hs_misc_conn_chan_find_reqd(conn_handle, BLE_L2CAP_CID_SM,
                                          &conn, &chan);
@@ -46,7 +46,7 @@ ble_l2cap_sm_tx(uint16_t conn_handle, struct os_mbuf *txom)
         rc = ble_l2cap_tx(conn, chan, txom);
     }
 
-    ble_hs_conn_unlock();
+    ble_hs_unlock();
 
     return rc;
 }
@@ -257,19 +257,22 @@ ble_l2cap_sm_pair_fail_write(void *payload, int len,
 }
 
 int
-ble_l2cap_sm_pair_fail_tx(uint16_t conn_handle,
-                          struct ble_l2cap_sm_pair_fail *cmd)
+ble_l2cap_sm_pair_fail_tx(uint16_t conn_handle, uint8_t reason)
 {
+    struct ble_l2cap_sm_pair_fail cmd;
     struct os_mbuf *txom;
     int rc;
 
+    BLE_HS_DBG_ASSERT(reason > 0 && reason < BLE_L2CAP_SM_ERR_MAX_PLUS_1);
+
     rc = ble_l2cap_sm_init_req(BLE_L2CAP_SM_PAIR_FAIL_SZ, &txom);
     if (rc != 0) {
         rc = BLE_HS_ENOMEM;
         goto done;
     }
 
-    ble_l2cap_sm_pair_fail_write(txom->om_data, txom->om_len, cmd);
+    cmd.reason = reason;
+    ble_l2cap_sm_pair_fail_write(txom->om_data, txom->om_len, &cmd);
 
     rc = ble_l2cap_sm_tx(conn_handle, txom);
     txom = NULL;

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e32f9f9f/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 a52d8b6..31eb108 100644
--- a/net/nimble/host/src/host_hci.c
+++ b/net/nimble/host/src/host_hci.c
@@ -31,11 +31,11 @@
 _Static_assert(sizeof (struct hci_data_hdr) == BLE_HCI_DATA_HDR_SZ,
                "struct hci_data_hdr must be 4 bytes");
 
+#define BLE_HCI_TIMEOUT       (OS_TICKS_PER_SEC)
+
 typedef int host_hci_event_fn(uint8_t event_code, uint8_t *data, int len);
 static host_hci_event_fn host_hci_rx_disconn_complete;
 static host_hci_event_fn host_hci_rx_encrypt_change;
-static host_hci_event_fn host_hci_rx_cmd_complete;
-static host_hci_event_fn host_hci_rx_cmd_status;
 static host_hci_event_fn host_hci_rx_num_completed_pkts;
 static host_hci_event_fn host_hci_rx_le_meta;
 
@@ -58,13 +58,8 @@ struct host_hci_stats
     uint32_t unknown_events_rxd;
 };
 
-/** The opcode of the current unacked HCI command; 0 if none. */
-uint16_t host_hci_outstanding_opcode;
-
 #define HOST_HCI_TIMEOUT        50      /* Milliseconds. */
 
-static struct os_callout_func host_hci_timer;
-
 /** Dispatch table for incoming HCI events.  Sorted by event code field. */
 struct host_hci_event_dispatch_entry {
     uint8_t hed_event_code;
@@ -74,8 +69,6 @@ struct host_hci_event_dispatch_entry {
 static const struct host_hci_event_dispatch_entry host_hci_event_dispatch[] = {
     { BLE_HCI_EVCODE_DISCONN_CMP, host_hci_rx_disconn_complete },
     { BLE_HCI_EVCODE_ENCRYPT_CHG, host_hci_rx_encrypt_change },
-    { BLE_HCI_EVCODE_COMMAND_COMPLETE, host_hci_rx_cmd_complete },
-    { BLE_HCI_EVCODE_COMMAND_STATUS, host_hci_rx_cmd_status },
     { BLE_HCI_EVCODE_NUM_COMP_PKTS, host_hci_rx_num_completed_pkts },
     { BLE_HCI_EVCODE_LE_META, host_hci_rx_le_meta },
 };
@@ -101,6 +94,24 @@ static const struct host_hci_le_event_dispatch_entry
 #define HOST_HCI_LE_EVENT_DISPATCH_SZ \
     (sizeof host_hci_le_event_dispatch / sizeof host_hci_le_event_dispatch[0])
 
+uint16_t
+host_hci_opcode_join(uint8_t ogf, uint16_t ocf)
+{
+    return (ogf << 10) | ocf;
+}
+
+uint16_t
+host_hci_handle_pb_bc_join(uint16_t handle, uint8_t pb, uint8_t bc)
+{
+    BLE_HS_DBG_ASSERT(handle <= 0x0fff);
+    BLE_HS_DBG_ASSERT(pb <= 0x03);
+    BLE_HS_DBG_ASSERT(bc <= 0x03);
+
+    return (handle  << 0)   |
+           (pb      << 12)  |
+           (bc      << 14);
+}
+
 static const struct host_hci_event_dispatch_entry *
 host_hci_dispatch_entry_find(uint8_t event_code)
 {
@@ -133,39 +144,6 @@ host_hci_le_dispatch_entry_find(uint8_t event_code)
     return NULL;
 }
 
-void
-host_hci_timer_set(void)
-{
-    int rc;
-
-    rc = os_callout_reset(&host_hci_timer.cf_c,
-                          HOST_HCI_TIMEOUT * OS_TICKS_PER_SEC / 1000);
-    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
-}
-
-static void
-host_hci_timer_stop(void)
-{
-    os_callout_stop(&host_hci_timer.cf_c);
-}
-
-static void
-host_hci_timer_exp(void *arg)
-{
-    struct ble_hci_ack ack;
-
-    BLE_HS_DBG_ASSERT(host_hci_outstanding_opcode != 0);
-
-    ack.bha_opcode = host_hci_outstanding_opcode;
-    ack.bha_status = BLE_HS_ETIMEOUT;
-    ack.bha_params = NULL;
-    ack.bha_params_len = 0;
-
-    host_hci_outstanding_opcode = 0;
-    ble_hci_sched_rx_ack(&ack);
-}
-
-
 static int
 host_hci_rx_disconn_complete(uint8_t event_code, uint8_t *data, int len)
 {
@@ -203,12 +181,13 @@ host_hci_rx_encrypt_change(uint8_t event_code, uint8_t *data, int len)
 }
 
 static int
-host_hci_rx_cmd_complete(uint8_t event_code, uint8_t *data, int len)
+host_hci_rx_cmd_complete(uint8_t event_code, uint8_t *data, int len,
+                         struct ble_hci_ack *out_ack)
 {
-    struct ble_hci_ack ack;
     uint16_t opcode;
-    uint8_t num_pkts;
     uint8_t *params;
+    uint8_t params_len;
+    uint8_t num_pkts;
 
     if (len < BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN) {
         /* XXX: Increment stat. */
@@ -222,37 +201,33 @@ host_hci_rx_cmd_complete(uint8_t event_code, uint8_t *data, int len)
     /* XXX: Process num_pkts field. */
     (void)num_pkts;
 
-    if (opcode != BLE_HCI_OPCODE_NOP &&
-        opcode != host_hci_outstanding_opcode) {
+    out_ack->bha_opcode = opcode;
 
-        STATS_INC(ble_hs_stats, hci_invalid_ack);
-        return BLE_HS_ENOENT;
-    }
-
-    if (opcode == host_hci_outstanding_opcode) {
-        /* Mark the outstanding command as acked. */
-        host_hci_outstanding_opcode = 0;
-        host_hci_timer_stop();
+    params_len = len - BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN;
+    if (params_len > 0) {
+        out_ack->bha_status = BLE_HS_HCI_ERR(params[0]);
+    } else if (opcode == BLE_HCI_OPCODE_NOP) {
+        out_ack->bha_status = 0;
+    } else {
+        out_ack->bha_status = BLE_HS_ECONTROLLER;
     }
 
-    ack.bha_opcode = opcode;
-    ack.bha_params = params;
-    ack.bha_params_len = len - BLE_HCI_EVENT_CMD_COMPLETE_HDR_LEN;
-    if (ack.bha_params_len > 0) {
-        ack.bha_status = BLE_HS_HCI_ERR(params[0]);
+    /* Don't include the status byte in the parameters blob. */
+    if (params_len > 1) {
+        out_ack->bha_params = params + 1;
+        out_ack->bha_params_len = params_len - 1;
     } else {
-        ack.bha_status = BLE_HS_ECONTROLLER;
+        out_ack->bha_params = NULL;
+        out_ack->bha_params_len = 0;
     }
 
-    ble_hci_sched_rx_ack(&ack);
-
     return 0;
 }
 
 static int
-host_hci_rx_cmd_status(uint8_t event_code, uint8_t *data, int len)
+host_hci_rx_cmd_status(uint8_t event_code, uint8_t *data, int len,
+                       struct ble_hci_ack *out_ack)
 {
-    struct ble_hci_ack ack;
     uint16_t opcode;
     uint8_t num_pkts;
     uint8_t status;
@@ -269,26 +244,10 @@ host_hci_rx_cmd_status(uint8_t event_code, uint8_t *data, int len)
     /* XXX: Process num_pkts field. */
     (void)num_pkts;
 
-    /* XXX: This check might be overaggressive for the command status event. */
-    if (opcode != BLE_HCI_OPCODE_NOP &&
-        opcode != host_hci_outstanding_opcode) {
-
-        STATS_INC(ble_hs_stats, hci_invalid_ack);
-        return BLE_HS_ENOENT;
-    }
-
-    if (opcode == host_hci_outstanding_opcode) {
-        /* Mark the outstanding command as acked. */
-        host_hci_outstanding_opcode = 0;
-        host_hci_timer_stop();
-    }
-
-    ack.bha_opcode = opcode;
-    ack.bha_params = NULL;
-    ack.bha_params_len = 0;
-    ack.bha_status = BLE_HS_HCI_ERR(status);
-
-    ble_hci_sched_rx_ack(&ack);
+    out_ack->bha_opcode = opcode;
+    out_ack->bha_params = NULL;
+    out_ack->bha_params_len = 0;
+    out_ack->bha_status = BLE_HS_HCI_ERR(status);
 
     return 0;
 }
@@ -318,7 +277,9 @@ host_hci_rx_num_completed_pkts(uint8_t event_code, uint8_t *data, int len)
         handle = le16toh(data + off + 2 * i);
         num_pkts = le16toh(data + off + 2 * num_handles + 2 * i);
 
-        ble_hs_conn_rx_num_completed_pkts(handle, num_pkts);
+        /* XXX: Do something with these values. */
+        (void)handle;
+        (void)num_pkts;
     }
 
     return 0;
@@ -596,7 +557,7 @@ host_hci_event_rx(uint8_t *data)
     int rc;
 
     /* Count events received */
-    STATS_INC(ble_hs_stats, hci_cmd);
+    STATS_INC(ble_hs_stats, hci_event);
 
     /* Display to console */
     host_hci_dbg_event_disp(data);
@@ -637,28 +598,197 @@ host_hci_os_event_proc(struct os_event *ev)
     return rc;
 }
 
+static uint8_t *ble_hci_ack_ev;
+static struct os_sem ble_hci_sem;
+
+#if PHONY_HCI_ACKS
+static ble_hci_phony_ack_fn *ble_hci_phony_ack_cb;
+#endif
+
+#if PHONY_HCI_ACKS
+void
+ble_hci_set_phony_ack_cb(ble_hci_phony_ack_fn *cb)
+{
+    ble_hci_phony_ack_cb = cb;
+}
+#endif
+
 /* XXX: For now, put this here */
 int
 ble_hci_transport_ctlr_event_send(uint8_t *hci_ev)
 {
-    os_error_t err;
     struct os_event *ev;
+    os_error_t err;
+    int enqueue;
 
     BLE_HS_DBG_ASSERT(hci_ev != NULL);
 
-    /* Get an event structure off the queue */
-    ev = (struct os_event *)os_memblock_get(&g_hci_os_event_pool);
-    if (!ev) {
-        err = os_memblock_put(&g_hci_cmd_pool, hci_ev);
-        BLE_HS_DBG_ASSERT_EVAL(err == OS_OK);
-        return -1;
+    switch (hci_ev[0]) {
+    case BLE_HCI_EVCODE_COMMAND_COMPLETE:
+    case BLE_HCI_EVCODE_COMMAND_STATUS:
+        if (hci_ev[3] == 0 && hci_ev[4] == 0) {
+            enqueue = 1;
+        } else {
+            if (ble_hci_ack_ev != NULL) {
+                /* The controller sent two acks.  Free the first one. */
+                BLE_HS_DBG_ASSERT(0);
+
+                err = os_memblock_put(&g_hci_cmd_pool, ble_hci_ack_ev);
+                BLE_HS_DBG_ASSERT_EVAL(err == OS_OK);
+            }
+
+            ble_hci_ack_ev = hci_ev;
+            os_sem_release(&ble_hci_sem);
+            enqueue = 0;
+        }
+        break;
+
+    default:
+        enqueue = 1;
+        break;
     }
 
-    /* Fill out the event and post to Link Layer */
-    ev->ev_queued = 0;
-    ev->ev_type = BLE_HOST_HCI_EVENT_CTLR_EVENT;
-    ev->ev_arg = hci_ev;
-    os_eventq_put(&ble_hs_evq, ev);
+    if (enqueue) {
+        /* Get an event structure off the queue */
+        ev = (struct os_event *)os_memblock_get(&g_hci_os_event_pool);
+        if (!ev) {
+            err = os_memblock_put(&g_hci_cmd_pool, hci_ev);
+            BLE_HS_DBG_ASSERT_EVAL(err == OS_OK);
+            return -1;
+        }
+
+        /* Fill out the event and post to host task. */
+        ev->ev_queued = 0;
+        ev->ev_type = BLE_HOST_HCI_EVENT_CTLR_EVENT;
+        ev->ev_arg = hci_ev;
+        ble_hs_event_enqueue(ev);
+    }
+
+    return 0;
+}
+
+static int
+ble_hci_process_ack(uint8_t *params_buf, uint8_t params_buf_len,
+                    struct ble_hci_ack *out_ack)
+{
+    uint8_t event_code;
+    uint8_t param_len;
+    uint8_t event_len;
+    int rc;
+
+    BLE_HS_DBG_ASSERT(ble_hci_ack_ev != NULL);
+
+    /* Count events received */
+    STATS_INC(ble_hs_stats, hci_event);
+
+    /* Display to console */
+    host_hci_dbg_event_disp(ble_hci_ack_ev);
+
+    event_code = ble_hci_ack_ev[0];
+    param_len = ble_hci_ack_ev[1];
+    event_len = param_len + 2;
+
+    /* Clear ack fields up front to silence spurious gcc warnings. */
+    memset(out_ack, 0, sizeof *out_ack);
+
+    switch (event_code) {
+    case BLE_HCI_EVCODE_COMMAND_COMPLETE:
+        rc = host_hci_rx_cmd_complete(event_code, ble_hci_ack_ev, event_len,
+                                      out_ack);
+        break;
+
+    case BLE_HCI_EVCODE_COMMAND_STATUS:
+        rc = host_hci_rx_cmd_status(event_code, ble_hci_ack_ev, event_len,
+                                    out_ack);
+        break;
+
+    default:
+        BLE_HS_DBG_ASSERT(0);
+        rc = BLE_HS_EUNKNOWN;
+        break;
+    }
+
+    if (rc == 0) {
+        if (params_buf == NULL) {
+            out_ack->bha_params_len = 0;
+        } else {
+            if (out_ack->bha_params_len > params_buf_len) {
+                out_ack->bha_params_len = params_buf_len;
+                rc = BLE_HS_EMSGSIZE;
+            }
+            memcpy(params_buf, out_ack->bha_params, out_ack->bha_params_len);
+        }
+        out_ack->bha_params = params_buf;
+    }
+
+    os_memblock_put(&g_hci_cmd_pool, ble_hci_ack_ev);
+    ble_hci_ack_ev = NULL;
+
+    return rc;
+}
+
+static int
+ble_hci_wait_for_ack(void)
+{
+    int rc;
+
+#if PHONY_HCI_ACKS
+    if (ble_hci_phony_ack_cb == NULL) {
+        rc = BLE_HS_ETIMEOUT;
+    } else {
+        BLE_HS_DBG_ASSERT(ble_hci_ack_ev == NULL);
+        ble_hci_ack_ev = os_memblock_get(&g_hci_cmd_pool);
+        if (ble_hci_ack_ev == NULL) {
+            rc = BLE_HS_ENOMEM;
+        } else {
+            rc = ble_hci_phony_ack_cb(ble_hci_ack_ev, 260);
+        }
+    }
+#else
+    rc = os_sem_pend(&ble_hci_sem, BLE_HCI_TIMEOUT);
+#endif
+
+    return rc;
+}
+
+int
+ble_hci_tx_cmd(void *cmd, void *evt_buf, uint8_t evt_buf_len,
+               uint8_t *out_evt_buf_len)
+{
+    struct ble_hci_ack ack;
+    int rc;
+
+    rc = host_hci_cmd_send_buf(cmd);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_hci_wait_for_ack();
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = ble_hci_process_ack(evt_buf, evt_buf_len, &ack);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (out_evt_buf_len != NULL) {
+        *out_evt_buf_len = ack.bha_params_len;
+    }
+
+    return ack.bha_status;
+}
+
+int
+ble_hci_tx_cmd_empty_ack(void *cmd)
+{
+    int rc;
+
+    rc = ble_hci_tx_cmd(cmd, NULL, 0, NULL);
+    if (rc != 0) {
+        return rc;
+    }
 
     return 0;
 }
@@ -717,7 +847,7 @@ host_hci_data_rx(struct os_mbuf *om)
         } else {
             handle = BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc);
 
-            ble_hs_conn_lock();
+            ble_hs_lock();
 
             conn = ble_hs_conn_find(handle);
             if (conn == NULL) {
@@ -727,7 +857,7 @@ host_hci_data_rx(struct os_mbuf *om)
                 om = NULL;
             }
 
-            ble_hs_conn_unlock();
+            ble_hs_unlock();
         }
     }
 
@@ -746,24 +876,6 @@ host_hci_data_rx(struct os_mbuf *om)
     return rc;
 }
 
-uint16_t
-host_hci_opcode_join(uint8_t ogf, uint16_t ocf)
-{
-    return (ogf << 10) | ocf;
-}
-
-uint16_t
-host_hci_handle_pb_bc_join(uint16_t handle, uint8_t pb, uint8_t bc)
-{
-    BLE_HS_DBG_ASSERT(handle <= 0x0fff);
-    BLE_HS_DBG_ASSERT(pb <= 0x03);
-    BLE_HS_DBG_ASSERT(bc <= 0x03);
-
-    return (handle  << 0)   |
-           (pb      << 12)  |
-           (bc      << 14);
-}
-
 static struct os_mbuf *
 host_hci_data_hdr_prepend(struct os_mbuf *om, uint16_t handle, uint8_t pb_flag)
 {
@@ -820,8 +932,8 @@ host_hci_data_tx(struct ble_hs_conn *connection, struct os_mbuf *om)
 void
 host_hci_init(void)
 {
-    host_hci_outstanding_opcode = 0;
+    int rc;
 
-    os_callout_func_init(&host_hci_timer, &ble_hs_evq,
-                         host_hci_timer_exp, NULL);
+    rc = os_sem_init(&ble_hci_sem, 0);
+    BLE_HS_DBG_ASSERT_EVAL(rc == 0);
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/e32f9f9f/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 dd7e14d..180571d 100644
--- a/net/nimble/host/src/host_hci_cmd.c
+++ b/net/nimble/host/src/host_hci_cmd.c
@@ -63,9 +63,6 @@ host_hci_cmd_send(uint8_t ogf, uint8_t ocf, uint8_t len, void *cmddata)
     uint8_t *cmd;
     uint16_t opcode;
 
-    /* Don't allow multiple commands "in flight." */
-    BLE_HS_DBG_ASSERT(host_hci_outstanding_opcode == 0);
-
     rc = -1;
     cmd = os_memblock_get(&g_hci_cmd_pool);
     if (cmd) {
@@ -78,15 +75,11 @@ host_hci_cmd_send(uint8_t ogf, uint8_t ocf, uint8_t len, void *cmddata)
         rc = host_hci_cmd_transport(cmd);
         BLE_HS_LOG(DEBUG, "host_hci_cmd_send: ogf=0x%02x ocf=0x%02x len=%d\n",
                    ogf, ocf, len);
-        if (rc == 0) {
-            host_hci_outstanding_opcode = opcode;
-        }
+        ble_hs_misc_log_flat_buf(cmd, len + BLE_HCI_CMD_HDR_LEN);
+        BLE_HS_LOG(DEBUG, "\n");
     }
 
-    /* Cancel ack callback if transmission failed. */
-    if (rc != 0) {
-        ble_hci_sched_set_ack_cb(NULL, NULL);
-    } else {
+    if (rc == 0) {
         STATS_INC(ble_hs_stats, hci_cmd);
     }
 
@@ -463,13 +456,34 @@ host_hci_cmd_rd_bd_addr(void)
     return rc;
 }
 
+static void
+host_hci_cmd_body_set_event_mask(uint64_t event_mask, uint8_t *dst)
+{
+    htole64(dst, event_mask);
+}
+
+void
+host_hci_cmd_build_set_event_mask(uint64_t event_mask,
+                                  uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_EVENT_MASK_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND,
+                       BLE_HCI_OCF_CB_SET_EVENT_MASK,
+                       BLE_HCI_SET_EVENT_MASK_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_set_event_mask(event_mask, dst);
+}
+
 int
 host_hci_cmd_set_event_mask(uint64_t event_mask)
 {
     int rc;
     uint8_t cmd[BLE_HCI_SET_EVENT_MASK_LEN];
 
-    htole64(cmd, event_mask);
+    host_hci_cmd_body_set_event_mask(event_mask, cmd);
     rc = host_hci_cmd_send(BLE_HCI_OGF_CTLR_BASEBAND,
                            BLE_HCI_OCF_CB_SET_EVENT_MASK,
                            BLE_HCI_SET_EVENT_MASK_LEN,
@@ -524,13 +538,35 @@ host_hci_cmd_rd_rem_version(uint16_t handle)
     return rc;
 }
 
+static void
+host_hci_cmd_body_le_set_event_mask(uint64_t event_mask, uint8_t *dst)
+{
+    htole64(dst, event_mask);
+}
+
+void
+host_hci_cmd_build_le_set_event_mask(uint64_t event_mask,
+                                     uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_SET_LE_EVENT_MASK_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE,
+                       BLE_HCI_OCF_LE_SET_EVENT_MASK,
+                       BLE_HCI_SET_LE_EVENT_MASK_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_le_set_event_mask(event_mask, dst);
+}
+
 int
 host_hci_cmd_le_set_event_mask(uint64_t event_mask)
 {
+    uint8_t cmd[BLE_HCI_SET_LE_EVENT_MASK_LEN];
+
     int rc;
-    uint8_t cmd[sizeof(uint64_t)];
 
-    htole64(cmd, event_mask);
+    host_hci_cmd_body_le_set_event_mask(event_mask, cmd);
     rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_SET_EVENT_MASK, sizeof(uint64_t),
                               cmd);
 
@@ -545,6 +581,21 @@ host_hci_cmd_le_set_event_mask(uint64_t event_mask)
  *
  * @return int
  */
+void
+host_hci_cmd_build_le_read_buffer_size(uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN);
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_BUF_SIZE, 0, dst);
+}
+
+/**
+ * LE Read buffer size
+ *  
+ * OGF = 0x08 (LE) 
+ * OCF = 0x0002 
+ * 
+ * @return int 
+ */
 int
 host_hci_cmd_le_read_buffer_size(void)
 {
@@ -642,6 +693,17 @@ host_hci_cmd_le_write_sugg_datalen(uint16_t txoctets, uint16_t txtime)
 /**
  * OGF=LE, OCF=0x0003
  */
+void
+host_hci_cmd_build_le_read_loc_supp_feat(uint8_t *dst, uint8_t dst_len)
+{
+    BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN);
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RD_LOC_SUPP_FEAT,
+                       0, dst);
+}
+
+/**
+ * OGF=LE, OCF=0x0003
+ */
 int
 host_hci_cmd_le_read_loc_supp_feat(void)
 {
@@ -668,7 +730,7 @@ host_hci_cmd_le_read_rem_used_feat(uint16_t handle)
 }
 
 static void
-host_hci_cmd_le_body_set_adv_enable(uint8_t enable, uint8_t *dst)
+host_hci_cmd_body_le_set_adv_enable(uint8_t enable, uint8_t *dst)
 {
     dst[0] = enable;
 }
@@ -684,7 +746,7 @@ host_hci_cmd_build_le_set_adv_enable(uint8_t enable, uint8_t *dst,
                        BLE_HCI_SET_ADV_ENABLE_LEN, dst);
     dst += BLE_HCI_CMD_HDR_LEN;
 
-    host_hci_cmd_le_body_set_adv_enable(enable, dst);
+    host_hci_cmd_body_le_set_adv_enable(enable, dst);
 }
 
 int
@@ -693,7 +755,7 @@ host_hci_cmd_le_set_adv_enable(uint8_t enable)
     int rc;
     uint8_t cmd[BLE_HCI_SET_ADV_ENABLE_LEN];
 
-    host_hci_cmd_le_body_set_adv_enable(enable, cmd);
+    host_hci_cmd_body_le_set_adv_enable(enable, cmd);
     rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_SET_ADV_ENABLE,
                               BLE_HCI_SET_ADV_ENABLE_LEN, cmd);
 
@@ -936,7 +998,7 @@ host_hci_cmd_le_create_connection(struct hci_create_conn *hcc)
 }
 
 void
-host_hci_cmd_le_build_clear_whitelist(uint8_t *dst, int dst_len)
+host_hci_cmd_build_le_clear_whitelist(uint8_t *dst, int dst_len)
 {
     BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN);
     host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLEAR_WHITE_LIST,
@@ -1031,6 +1093,14 @@ host_hci_cmd_le_rmv_from_whitelist(uint8_t *addr, uint8_t addr_type)
     return rc;
 }
 
+void
+host_hci_cmd_build_reset(uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN);
+    host_hci_write_hdr(BLE_HCI_OGF_CTLR_BASEBAND, BLE_HCI_OCF_CB_RESET,
+                       0, dst);
+}
+
 /**
  * Reset the controller and link manager.
  *
@@ -1143,6 +1213,42 @@ host_hci_cmd_le_conn_update(struct hci_conn_update *hcu)
     return 0;
 }
 
+static void
+host_hci_cmd_body_le_lt_key_req_reply(struct hci_lt_key_req_reply *hkr,
+                                      uint8_t *dst)
+{
+    htole16(dst + 0, hkr->conn_handle);
+    memcpy(dst + 2, hkr->long_term_key, sizeof hkr->long_term_key);
+}
+
+/**
+ * Sends the long-term key (LTK) to the controller.
+ *
+ * Note: This function expects the 128-bit key to be in little-endian byte
+ * order.
+ *
+ * OGF = 0x08 (LE)
+ * OCF = 0x001a
+ *
+ * @param key
+ * @param pt
+ *
+ * @return int
+ */
+void
+host_hci_cmd_build_le_lt_key_req_reply(struct hci_lt_key_req_reply *hkr,
+                                       uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LT_KEY_REQ_REPLY_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY,
+                       BLE_HCI_LT_KEY_REQ_REPLY_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_le_lt_key_req_reply(hkr, dst);
+}
+
 /**
  * Sends the long-term key (LTK) to the controller.
  *
@@ -1163,14 +1269,39 @@ host_hci_cmd_le_lt_key_req_reply(struct hci_lt_key_req_reply *hkr)
     uint8_t cmd[BLE_HCI_LT_KEY_REQ_REPLY_LEN];
     int rc;
 
-    htole16(cmd + 0, hkr->conn_handle);
-    memcpy(cmd + 2, hkr->long_term_key, sizeof hkr->long_term_key);
-
+    host_hci_cmd_body_le_lt_key_req_reply(hkr, cmd);
     rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_LT_KEY_REQ_REPLY,
                               sizeof cmd, cmd);
     return rc;
 }
 
+static void
+host_hci_cmd_body_le_conn_param_reply(struct hci_conn_param_reply *hcr,
+                                      uint8_t *dst)
+{
+    htole16(dst + 0, hcr->handle);
+    htole16(dst + 2, hcr->conn_itvl_min);
+    htole16(dst + 4, hcr->conn_itvl_max);
+    htole16(dst + 6, hcr->conn_latency);
+    htole16(dst + 8, hcr->supervision_timeout);
+    htole16(dst + 10, hcr->min_ce_len);
+    htole16(dst + 12, hcr->max_ce_len);
+}
+
+void
+host_hci_cmd_build_le_conn_param_reply(struct hci_conn_param_reply *hcr,
+                                       uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_PARAM_REPLY_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_RR,
+                       BLE_HCI_CONN_PARAM_REPLY_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_le_conn_param_reply(hcr, dst);
+}
+
 int
 host_hci_cmd_le_lt_key_req_neg_reply(uint16_t handle)
 {
@@ -1202,14 +1333,36 @@ host_hci_cmd_le_conn_param_reply(struct hci_conn_param_reply *hcr)
     return rc;
 }
 
+static void
+host_hci_cmd_body_le_conn_param_neg_reply(struct hci_conn_param_neg_reply *hcn,
+                                          uint8_t *dst)
+{
+    htole16(dst + 0, hcn->handle);
+    dst[2] = hcn->reason;
+}
+
+
+void
+host_hci_cmd_build_le_conn_param_neg_reply(
+    struct hci_conn_param_neg_reply *hcn, uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_CONN_PARAM_NEG_REPLY_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR,
+                       BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_le_conn_param_neg_reply(hcn, dst);
+}
+
 int
 host_hci_cmd_le_conn_param_neg_reply(struct hci_conn_param_neg_reply *hcn)
 {
     uint8_t cmd[BLE_HCI_CONN_PARAM_NEG_REPLY_LEN];
     int rc;
 
-    htole16(cmd + 0, hcn->handle);
-    cmd[2] = hcn->reason;
+    host_hci_cmd_body_le_conn_param_neg_reply(hcn, cmd);
 
     rc = host_hci_le_cmd_send(BLE_HCI_OCF_LE_REM_CONN_PARAM_NRR,
                               BLE_HCI_CONN_PARAM_NEG_REPLY_LEN, cmd);
@@ -1284,12 +1437,50 @@ host_hci_cmd_le_encrypt(uint8_t *key, uint8_t *pt)
  *
  * @return int
  */
+void
+host_hci_cmd_build_le_rand(uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(dst_len >= BLE_HCI_CMD_HDR_LEN);
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RAND, 0, dst);
+}
+
+/**
+ * Get random data
+ *  
+ * OGF = 0x08 (LE) 
+ * OCF = 0x0018
+ *
+ * @return int
+ */
 int
 host_hci_cmd_le_rand(void)
 {
     return host_hci_le_cmd_send(BLE_HCI_OCF_LE_RAND, 0, NULL);
 }
 
+static void
+host_hci_cmd_body_le_start_encrypt(struct hci_start_encrypt *cmd, uint8_t *dst)
+{
+    htole16(dst + 0, cmd->connection_handle);
+    htole64(dst + 2, cmd->random_number);
+    htole16(dst + 10, cmd->encrypted_diversifier);
+    memcpy(dst + 12, cmd->long_term_key, sizeof cmd->long_term_key);
+}
+
+void
+host_hci_cmd_build_le_start_encrypt(struct hci_start_encrypt *cmd,
+                                    uint8_t *dst, int dst_len)
+{
+    BLE_HS_DBG_ASSERT(
+        dst_len >= BLE_HCI_CMD_HDR_LEN + BLE_HCI_LE_START_ENCRYPT_LEN);
+
+    host_hci_write_hdr(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_START_ENCRYPT,
+                       BLE_HCI_LE_START_ENCRYPT_LEN, dst);
+    dst += BLE_HCI_CMD_HDR_LEN;
+
+    host_hci_cmd_body_le_start_encrypt(cmd, dst);
+}
+
 /**
  * Enables encryption on a connection.
  *
@@ -1303,10 +1494,7 @@ host_hci_cmd_le_start_encrypt(struct hci_start_encrypt *cmd)
 {
     uint8_t buf[BLE_HCI_LE_START_ENCRYPT_LEN];
 
-    htole16(buf + 0, cmd->connection_handle);
-    htole64(buf + 2, cmd->random_number);
-    htole16(buf + 10, cmd->encrypted_diversifier);
-    memcpy(buf + 12, cmd->long_term_key, sizeof cmd->long_term_key);
+    host_hci_cmd_body_le_start_encrypt(cmd, buf);
 
     return host_hci_le_cmd_send(BLE_HCI_OCF_LE_START_ENCRYPT, sizeof buf, buf);
 }