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

[15/16] incubator-mynewt-core git commit: BLE Host - Wait for conn-cmplt-ev for GAP conn tmo

BLE Host - Wait for conn-cmplt-ev for GAP conn tmo

Prior to this change, the host had the following behavior when a GAP
connect procedure timed out:

1. Send a cancel create connection HCI command to the controller.
2. Wait for the command status event from the controller.
3. Stop the connection-timeout timer.
4. Indicate to the application that the connect procedure has timed out.
5. Receive the connection complete event from the controller indicating
that the cancel procedure succeeded.

This was problematic for a number of reasons:

* The necessary context was discarded before the impending connection
complete event was received from the controller.  When the event arrived
(step 5), the host would interpret it as if the application had manually
cancelled a connect procedure in progress.

* If the application attempted another master procedure before the
controller cancelled the connect procedure, the controller would report
a failure with status 0x0c (Command Disallowed).  This happened because
the controller cannot perform two connect procedures at the same time.

* If the application attempted another master procedure after the
controller cancelled the connect procedure, but before the host received
the connection complete event, the timer for the second procedure would
never get set.  Consequently, the second procedure would never time out.

The new behavior is:

1. Send a cancel create connection HCI command to the controller.
2. Wait for the command status event from the controller.
3. Stop the connection-timeout timer.
4. Receive the connection complete event from the controller indicating
that the cancel procedure succeeded.
5. Indicate to the application that the connect procedure has timed out.


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/db980b24
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/db980b24
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/db980b24

Branch: refs/heads/sterly_refactor
Commit: db980b24e5e66a5d9a038fd4ac43c9c6f65911bc
Parents: b731555
Author: Christopher Collins <cc...@apache.org>
Authored: Sat Jul 30 17:29:38 2016 -0700
Committer: Sterling Hughes <st...@apache.org>
Committed: Sun Jul 31 21:58:57 2016 -0700

----------------------------------------------------------------------
 net/nimble/host/src/ble_gap.c               |  95 +++---
 net/nimble/host/src/test/ble_gap_test.c     | 386 +++++++++++++++++------
 net/nimble/host/src/test/ble_hs_test_util.c |  98 ++++++
 net/nimble/host/src/test/ble_hs_test_util.h |   3 +
 4 files changed, 441 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/db980b24/net/nimble/host/src/ble_gap.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index c8d0383..a001ae3 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -94,7 +94,7 @@ static const struct ble_gap_conn_params ble_gap_conn_params_dflt = {
  * The state of the in-progress master connection.  If no master connection is
  * currently in progress, then the op field is set to BLE_GAP_OP_NULL.
  */
-static bssnz_t struct {
+struct ble_gap_master_state {
     uint8_t op;
 
     uint8_t exp_set:1;
@@ -108,13 +108,15 @@ static bssnz_t struct {
         struct {
             uint8_t using_wl:1;
             uint8_t our_addr_type:2;
+            uint8_t cancel:1;
         } conn;
 
         struct {
             uint8_t limited:1;
         } disc;
     };
-} ble_gap_master;
+};
+static bssnz_t struct ble_gap_master_state ble_gap_master;
 
 /**
  * The state of the in-progress slave connection.  If no slave connection is
@@ -462,6 +464,7 @@ ble_gap_master_reset_state(void)
 {
     ble_gap_master.op = BLE_GAP_OP_NULL;
     ble_gap_master.exp_set = 0;
+    ble_gap_master.conn.cancel = 0;
 }
 
 static void
@@ -472,13 +475,12 @@ ble_gap_slave_reset_state(void)
 }
 
 static void
-ble_gap_master_extract_cb(ble_gap_event_fn **out_cb, void **out_cb_arg,
-                          int reset_state)
+ble_gap_master_extract_state(struct ble_gap_master_state *out_state,
+                             int reset_state)
 {
     ble_hs_lock();
 
-    *out_cb = ble_gap_master.cb;
-    *out_cb_arg = ble_gap_master.cb_arg;
+    *out_state = ble_gap_master;
 
     if (reset_state) {
         ble_gap_master_reset_state();
@@ -518,18 +520,17 @@ ble_gap_adv_finished(void)
 static int
 ble_gap_master_connect_failure(int status)
 {
+    struct ble_gap_master_state state;
     struct ble_gap_event event;
-    ble_gap_event_fn *cb;
-    void *cb_arg;
     int rc;
 
-    ble_gap_master_extract_cb(&cb, &cb_arg, 1);
-    if (cb != NULL) {
+    ble_gap_master_extract_state(&state, 1);
+    if (state.cb != NULL) {
         memset(&event, 0, sizeof event);
         event.type = BLE_GAP_EVENT_CONNECT;
         event.connect.status = status;
 
-        rc = cb(&event, cb_arg);
+        rc = state.cb(&event, state.cb_arg);
     } else {
         rc = 0;
     }
@@ -537,58 +538,61 @@ ble_gap_master_connect_failure(int status)
     return rc;
 }
 
-static int
-ble_gap_master_connect_cancel(void)
+static void
+ble_gap_master_connect_cancelled(void)
 {
+    struct ble_gap_master_state state;
     struct ble_gap_event event;
-    ble_gap_event_fn *cb;
-    void *cb_arg;
-    int rc;
 
-    ble_gap_master_extract_cb(&cb, &cb_arg, 1);
-    if (cb != NULL) {
+    ble_gap_master_extract_state(&state, 1);
+    if (state.cb != NULL) {
+        /* The GAP event type depends on whether 1) the application manually
+         * cancelled the connect procedure or 2) the connect procedure timed
+         * out.
+         */
         memset(&event, 0, sizeof event);
-        event.type = BLE_GAP_EVENT_CONN_CANCEL;
-        rc = cb(&event, cb_arg);
-    } else {
-        rc = 0;
-    }
+        if (state.conn.cancel) {
+            event.type = BLE_GAP_EVENT_CONN_CANCEL;
+        } else {
+            event.type = BLE_GAP_EVENT_CONNECT;
+            event.connect.status = BLE_HS_ETIMEOUT;
+            event.connect.conn_handle = BLE_HS_CONN_HANDLE_NONE;
 
-    return rc;
+        }
+        state.cb(&event, state.cb_arg);
+    }
 }
 
 static void
 ble_gap_disc_report(struct ble_gap_disc_desc *desc)
 {
+    struct ble_gap_master_state state;
     struct ble_gap_event event;
-    ble_gap_event_fn *cb;
-    void *cb_arg;
 
-    ble_gap_master_extract_cb(&cb, &cb_arg, 0);
+    ble_gap_master_extract_state(&state, 0);
 
-    if (cb != NULL) {
+    if (state.cb != NULL) {
         memset(&event, 0, sizeof event);
         event.type = BLE_GAP_EVENT_DISC;
         event.disc = *desc;
 
-        cb(&event, cb_arg);
+        state.cb(&event, state.cb_arg);
     }
 }
 
 static void
 ble_gap_disc_complete(void)
 {
+    struct ble_gap_master_state state;
     struct ble_gap_event event;
-    ble_gap_event_fn *cb;
-    void *cb_arg;
 
-    ble_gap_master_extract_cb(&cb, &cb_arg, 1);
+    ble_gap_master_extract_state(&state, 1);
 
-    if (cb != NULL) {
+    if (state.cb != NULL) {
         memset(&event, 0, sizeof event);
         event.type = BLE_GAP_EVENT_DISC_COMPLETE;
 
-        ble_gap_call_event_cb(&event, cb, cb_arg);
+        ble_gap_call_event_cb(&event, state.cb, state.cb_arg);
     }
 }
 
@@ -951,7 +955,9 @@ ble_gap_rx_conn_complete(struct hci_le_conn_complete *evt)
     STATS_INC(ble_gap_stats, rx_conn_complete);
 
     /* Apply the event to the existing connection if it exists. */
-    if (ble_hs_atomic_conn_flags(evt->connection_handle, NULL) == 0) {
+    if (evt->status != BLE_ERR_UNK_CONN_ID &&
+        ble_hs_atomic_conn_flags(evt->connection_handle, NULL) == 0) {
+
         /* XXX: Does this ever happen? */
 
         if (evt->status != 0) {
@@ -976,7 +982,7 @@ ble_gap_rx_conn_complete(struct hci_le_conn_complete *evt)
             if (ble_gap_master_in_progress()) {
                 if (evt->status == BLE_ERR_UNK_CONN_ID) {
                     /* Connect procedure successfully cancelled. */
-                    ble_gap_master_connect_cancel();
+                    ble_gap_master_connect_cancelled();
                 } else {
                     ble_gap_master_failed(BLE_HS_HCI_ERR(evt->status));
                 }
@@ -1089,9 +1095,17 @@ ble_gap_master_heartbeat(void)
         if (rc != 0) {
             /* Failed to stop connecting; try again in 100 ms. */
             return BLE_GAP_CANCEL_RETRY_RATE;
+        } else {
+            /* Stop the timer now that the cancel command has been acked. */
+            ble_gap_master.exp_set = 0;
+
+            /* Timeout gets reported when we receive a connection complete
+             * event indicating the connect procedure has been cancelled.
+             */
+            /* XXX: Set a timer to reset the controller if a connection
+             * complete event isn't received within a reasonable interval.
+             */
         }
-
-        ble_gap_master_failed(BLE_HS_ETIMEOUT);
         break;
 
     case BLE_GAP_OP_M_DISC:
@@ -1928,7 +1942,7 @@ ble_gap_disc_cancel(void)
 
     ble_hs_lock();
 
-    if (ble_gap_master.op != BLE_GAP_OP_M_DISC) {
+    if (!ble_gap_disc_active()) {
         rc = BLE_HS_EALREADY;
         goto done;
     }
@@ -2396,7 +2410,7 @@ ble_gap_conn_cancel(void)
 
     ble_hs_lock();
 
-    if (!ble_gap_master_in_progress()) {
+    if (!ble_gap_conn_active()) {
         rc = BLE_HS_EALREADY;
         goto done;
     }
@@ -2408,6 +2422,7 @@ ble_gap_conn_cancel(void)
         goto done;
     }
 
+    ble_gap_master.conn.cancel = 1;
     rc = 0;
 
 done:

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/db980b24/net/nimble/host/src/test/ble_gap_test.c
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_gap_test.c b/net/nimble/host/src/test/ble_gap_test.c
index 86d0e6f..faf62c3 100644
--- a/net/nimble/host/src/test/ble_gap_test.c
+++ b/net/nimble/host/src/test/ble_gap_test.c
@@ -76,9 +76,12 @@ static int
 ble_gap_test_util_disc_cb(struct ble_gap_event *event, void *arg)
 {
     ble_gap_test_disc_event_type = event->type;
-    ble_gap_test_disc_desc = event->disc;
     ble_gap_test_disc_arg = arg;
 
+    if (event->type == BLE_GAP_EVENT_DISC) {
+        ble_gap_test_disc_desc = event->disc;
+    }
+
     return 0;
 }
 
@@ -207,66 +210,7 @@ ble_gap_test_util_verify_tx_scan_enable(uint8_t enable,
 }
 
 static void
-ble_gap_test_util_verify_tx_create_conn(const struct hci_create_conn *exp)
-{
-    uint8_t param_len;
-    uint8_t *param;
-
-    param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE,
-                                           BLE_HCI_OCF_LE_CREATE_CONN,
-                                           &param_len);
-    TEST_ASSERT(param_len == BLE_HCI_CREATE_CONN_LEN);
-
-    TEST_ASSERT(le16toh(param + 0) == exp->scan_itvl);
-    TEST_ASSERT(le16toh(param + 2) == exp->scan_window);
-    TEST_ASSERT(param[4] == exp->filter_policy);
-    TEST_ASSERT(param[5] == exp->peer_addr_type);
-    TEST_ASSERT(memcmp(param + 6, exp->peer_addr, 6) == 0);
-    TEST_ASSERT(param[12] == exp->own_addr_type);
-    TEST_ASSERT(le16toh(param + 13) == exp->conn_itvl_min);
-    TEST_ASSERT(le16toh(param + 15) == exp->conn_itvl_max);
-    TEST_ASSERT(le16toh(param + 17) == exp->conn_latency);
-    TEST_ASSERT(le16toh(param + 19) == exp->supervision_timeout);
-    TEST_ASSERT(le16toh(param + 21) == exp->min_ce_len);
-    TEST_ASSERT(le16toh(param + 23) == exp->max_ce_len);
-}
-
-static void
-ble_gap_test_util_hcc_from_conn_params(
-    struct hci_create_conn *hcc, uint8_t own_addr_type, uint8_t peer_addr_type,
-    const uint8_t *peer_addr, const struct ble_gap_conn_params *conn_params)
-{
-    hcc->scan_itvl = conn_params->scan_itvl;
-    hcc->scan_window = conn_params->scan_window;
-    hcc->filter_policy = peer_addr_type == BLE_GAP_ADDR_TYPE_WL ?
-        BLE_HCI_CONN_FILT_USE_WL :
-        BLE_HCI_CONN_FILT_NO_WL;
-    hcc->peer_addr_type = peer_addr_type;
-    memcpy(hcc->peer_addr, peer_addr, 6);
-    hcc->own_addr_type = own_addr_type;
-    hcc->conn_itvl_min = conn_params->itvl_min;
-    hcc->conn_itvl_max = conn_params->itvl_max;
-    hcc->conn_latency = conn_params->latency;
-    hcc->supervision_timeout = conn_params->supervision_timeout;
-    hcc->min_ce_len = conn_params->min_ce_len;
-    hcc->max_ce_len = conn_params->max_ce_len;
-}
-
-static void
-ble_gap_test_util_conn_params_dflt(struct ble_gap_conn_params *conn_params)
-{
-    conn_params->scan_itvl = 0x0010;
-    conn_params->scan_window = 0x0010;
-    conn_params->itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN;
-    conn_params->itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX;
-    conn_params->latency = BLE_GAP_INITIAL_CONN_LATENCY;
-    conn_params->supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT;
-    conn_params->min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
-    conn_params->max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
-}
-
-static void
-ble_gap_test_util_verify_tx_create_conn_cancel(void)
+ble_hs_test_util_verify_tx_create_conn_cancel(void)
 {
     uint8_t param_len;
 
@@ -838,7 +782,6 @@ TEST_CASE(ble_gap_test_case_conn_dir_good)
 {
     struct hci_le_conn_complete evt;
     struct ble_gap_conn_params params;
-    struct hci_create_conn hcc;
     int rc;
 
     uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 };
@@ -865,11 +808,6 @@ TEST_CASE(ble_gap_test_case_conn_dir_good)
     TEST_ASSERT(ble_gap_master_in_progress());
     TEST_ASSERT(ble_gap_conn_active());
 
-    /* Verify tx of create connection command. */
-    ble_gap_test_util_hcc_from_conn_params(&hcc, BLE_ADDR_TYPE_PUBLIC,
-                                           BLE_ADDR_TYPE_PUBLIC, peer_addr,
-                                           &params);
-    ble_gap_test_util_verify_tx_create_conn(&hcc);
     TEST_ASSERT(ble_gap_master_in_progress());
     TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == BLE_HS_ENOTCONN);
 
@@ -925,9 +863,6 @@ TEST_CASE(ble_gap_test_case_conn_dir_bad_args)
 TEST_CASE(ble_gap_test_case_conn_dir_dflt_params)
 {
     static const uint8_t peer_addr[6] = { 2, 3, 8, 6, 6, 1 };
-
-    struct ble_gap_conn_params conn_params;
-    struct hci_create_conn hcc;
     int rc;
 
     ble_gap_test_util_init();
@@ -936,12 +871,6 @@ TEST_CASE(ble_gap_test_case_conn_dir_dflt_params)
                                   BLE_ADDR_TYPE_PUBLIC, peer_addr, 0, NULL,
                                   ble_gap_test_util_connect_cb, NULL, 0);
     TEST_ASSERT(rc == 0);
-
-    ble_gap_test_util_conn_params_dflt(&conn_params);
-    ble_gap_test_util_hcc_from_conn_params(&hcc, BLE_ADDR_TYPE_PUBLIC,
-                                           BLE_ADDR_TYPE_PUBLIC, peer_addr,
-                                           &conn_params);
-    ble_gap_test_util_verify_tx_create_conn(&hcc);
 }
 
 TEST_SUITE(ble_gap_test_suite_conn_dir)
@@ -958,34 +887,17 @@ TEST_SUITE(ble_gap_test_suite_conn_dir)
  *****************************************************************************/
 
 static void
-ble_gap_test_util_conn_cancel(uint8_t *peer_addr, uint8_t hci_status)
+ble_gap_test_util_conn_cancel(uint8_t hci_status)
 {
     struct hci_le_conn_complete evt;
-    struct ble_gap_conn_params conn_params;
-    struct hci_create_conn hcc;
     int rc;
 
-    ble_gap_test_util_init();
-
-    /* Begin creating a connection. */
-    rc = ble_hs_test_util_connect(BLE_ADDR_TYPE_PUBLIC,
-                                  BLE_ADDR_TYPE_PUBLIC, peer_addr, 0, NULL,
-                                  ble_gap_test_util_connect_cb, NULL, 0);
-    TEST_ASSERT(rc == 0);
-    TEST_ASSERT(ble_gap_master_in_progress());
-
-    ble_gap_test_util_conn_params_dflt(&conn_params);
-    ble_gap_test_util_hcc_from_conn_params(&hcc, BLE_ADDR_TYPE_PUBLIC,
-                                           BLE_ADDR_TYPE_PUBLIC, peer_addr,
-                                           &conn_params);
-    ble_gap_test_util_verify_tx_create_conn(&hcc);
-
     /* Initiate cancel procedure. */
     rc = ble_hs_test_util_conn_cancel(hci_status);
     TEST_ASSERT(rc == BLE_HS_HCI_ERR(hci_status));
 
     /* Verify tx of cancel create connection command. */
-    ble_gap_test_util_verify_tx_create_conn_cancel();
+    ble_hs_test_util_verify_tx_create_conn_cancel();
     if (rc != 0) {
         return;
     }
@@ -995,12 +907,29 @@ ble_gap_test_util_conn_cancel(uint8_t *peer_addr, uint8_t hci_status)
     memset(&evt, 0, sizeof evt);
     evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;
     evt.status = BLE_ERR_UNK_CONN_ID;
-    evt.connection_handle = 2;
-    evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER;
-    memcpy(evt.peer_addr, peer_addr, 6);
     rc = ble_gap_rx_conn_complete(&evt);
     TEST_ASSERT(rc == 0);
     TEST_ASSERT(!ble_gap_master_in_progress());
+
+    TEST_ASSERT(ble_gap_test_conn_event_type == BLE_GAP_EVENT_CONN_CANCEL);
+}
+
+static void
+ble_gap_test_util_conn_and_cancel(uint8_t *peer_addr, uint8_t hci_status)
+{
+    int rc;
+
+    ble_gap_test_util_init();
+
+    /* Begin creating a connection. */
+    rc = ble_hs_test_util_connect(BLE_ADDR_TYPE_PUBLIC,
+                                  BLE_ADDR_TYPE_PUBLIC, peer_addr, 0, NULL,
+                                  ble_gap_test_util_connect_cb, NULL, 0);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(ble_gap_master_in_progress());
+
+    /* Initiate cancel procedure. */
+    ble_gap_test_util_conn_cancel(hci_status);
     TEST_ASSERT(ble_hs_atomic_conn_flags(2, NULL) == BLE_HS_ENOTCONN);
 }
 
@@ -1020,7 +949,7 @@ TEST_CASE(ble_gap_test_case_conn_cancel_good)
 {
     uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 };
 
-    ble_gap_test_util_conn_cancel(peer_addr, 0);
+    ble_gap_test_util_conn_and_cancel(peer_addr, 0);
 
     TEST_ASSERT(ble_gap_test_conn_event_type == BLE_GAP_EVENT_CONN_CANCEL);
     TEST_ASSERT(ble_gap_test_conn_desc.conn_handle == BLE_HS_CONN_HANDLE_NONE);
@@ -1033,7 +962,7 @@ TEST_CASE(ble_gap_test_case_conn_cancel_ctlr_fail)
 
     uint8_t peer_addr[6] = { 1, 2, 3, 4, 5, 6 };
 
-    ble_gap_test_util_conn_cancel(peer_addr, BLE_ERR_REPEATED_ATTEMPTS);
+    ble_gap_test_util_conn_and_cancel(peer_addr, BLE_ERR_REPEATED_ATTEMPTS);
 
     /* Make sure the host didn't invoke the application callback.  The cancel
      * failure was indicated via the return code from the gap call.
@@ -1583,7 +1512,8 @@ TEST_CASE(ble_gap_test_case_adv_ctlr_fail)
                                   peer_addr, c, d, BLE_ERR_DIR_ADV_TMO, -1, 0);
 
             TEST_ASSERT(!ble_gap_adv_active());
-            TEST_ASSERT(ble_gap_test_conn_event_type == BLE_GAP_EVENT_ADV_COMPLETE);
+            TEST_ASSERT(ble_gap_test_conn_event_type ==
+                        BLE_GAP_EVENT_ADV_COMPLETE);
             TEST_ASSERT(ble_gap_test_conn_desc.conn_handle ==
                         BLE_HS_CONN_HANDLE_NONE);
             TEST_ASSERT(ble_gap_test_conn_arg == NULL);
@@ -2280,6 +2210,259 @@ TEST_SUITE(ble_gap_test_suite_update_conn)
 }
 
 /*****************************************************************************
+ * $timeout                                                                  *
+ *****************************************************************************/
+
+static void
+ble_gap_test_util_conn_forever(void)
+{
+    int32_t ticks_from_now;
+
+    /* Initiate a connect procedure with no timeout. */
+    ble_hs_test_util_connect(BLE_ADDR_TYPE_PUBLIC,
+                             BLE_ADDR_TYPE_PUBLIC,
+                             ((uint8_t[]){ 1, 2, 3, 4, 5, 6 }), BLE_HS_FOREVER,
+                             NULL, ble_gap_test_util_connect_cb,
+                             NULL, 0);
+
+    /* Ensure no pending GAP event. */
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Advance 100 seconds; ensure no timeout reported. */
+    os_time_advance(100 * OS_TICKS_PER_SEC);
+    TEST_ASSERT(ble_gap_test_conn_event_type == -1);
+    TEST_ASSERT(ble_gap_conn_active());
+}
+
+static void
+ble_gap_test_util_conn_timeout(int32_t duration_ms)
+{
+    struct hci_le_conn_complete evt;
+    uint32_t duration_ticks;
+    int32_t ticks_from_now;
+    int rc;
+
+    TEST_ASSERT_FATAL(duration_ms != BLE_HS_FOREVER);
+
+    /* Initiate a connect procedure with the specified timeout. */
+    ble_hs_test_util_connect(BLE_ADDR_TYPE_PUBLIC,
+                             BLE_ADDR_TYPE_PUBLIC,
+                             ((uint8_t[]){ 1, 2, 3, 4, 5, 6 }), duration_ms,
+                             NULL, ble_gap_test_util_connect_cb,
+                             NULL, 0);
+
+    /* Ensure next GAP event is at the expected time. */
+    rc = os_time_ms_to_ticks(duration_ms, &duration_ticks);
+    TEST_ASSERT_FATAL(rc == 0);
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == duration_ticks);
+
+    /* Advance duration ms; ensure timeout event does not get reported before
+     * connection complete event rxed.
+     */
+    os_time_advance(duration_ms);
+
+    ble_hs_test_util_set_ack(
+        host_hci_opcode_join(BLE_HCI_OGF_LE,
+                             BLE_HCI_OCF_LE_CREATE_CONN_CANCEL),
+        0);
+
+    TEST_ASSERT(ble_gap_test_conn_event_type == -1);
+
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Ensure cancel create connection command was sent. */
+    ble_hs_test_util_verify_tx_create_conn_cancel();
+
+    /* Ensure timer has been stopped. */
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Receive the connection complete event indicating a successful cancel. */
+    memset(&evt, 0, sizeof evt);
+    evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;
+    evt.status = BLE_ERR_UNK_CONN_ID;
+    rc = ble_gap_rx_conn_complete(&evt);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    /* Ensure the GAP event was triggered. */
+    TEST_ASSERT(ble_gap_test_conn_event_type == BLE_GAP_EVENT_CONNECT);
+    TEST_ASSERT(ble_gap_test_conn_status == BLE_HS_ETIMEOUT);
+
+    /* Clear GAP event for remainder of test. */
+    ble_gap_test_util_reset_cb_info();
+}
+
+static void
+ble_gap_test_util_disc_forever(void)
+{
+    struct ble_gap_disc_params params;
+    int32_t ticks_from_now;
+
+    memset(&params, 0, sizeof params);
+
+    /* Initiate a discovery procedure with no timeout. */
+    ble_hs_test_util_disc(BLE_ADDR_TYPE_PUBLIC,
+                          BLE_HS_FOREVER, &params, ble_gap_test_util_disc_cb,
+                          NULL, -1, 0);
+
+    /* Ensure no pending GAP event. */
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    /* Advance 100 seconds; ensure no timeout reported. */
+    os_time_advance(100 * OS_TICKS_PER_SEC);
+    TEST_ASSERT(ble_gap_test_disc_event_type == -1);
+    TEST_ASSERT(ble_gap_disc_active());
+}
+
+static void
+ble_gap_test_util_disc_timeout(int32_t duration_ms)
+{
+    struct ble_gap_disc_params params;
+    uint32_t duration_ticks;
+    int32_t ticks_from_now;
+    int rc;
+
+    TEST_ASSERT_FATAL(duration_ms != BLE_HS_FOREVER);
+
+    memset(&params, 0, sizeof params);
+
+    /* Initiate a discovery procedure with the specified timeout. */
+    ble_hs_test_util_disc(BLE_ADDR_TYPE_PUBLIC,
+                          duration_ms, &params, ble_gap_test_util_disc_cb,
+                          NULL, -1, 0);
+
+    /* Ensure next GAP event is at the expected time. */
+    rc = os_time_ms_to_ticks(duration_ms, &duration_ticks);
+    TEST_ASSERT_FATAL(rc == 0);
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == duration_ticks);
+
+    /* Advance duration ms; ensure timeout event was reported. */
+    os_time_advance(duration_ms);
+
+    ble_hs_test_util_set_ack(
+        host_hci_opcode_join(BLE_HCI_OGF_LE,
+                             BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
+        0);
+    ticks_from_now = ble_gap_heartbeat();
+    TEST_ASSERT(ticks_from_now == BLE_HS_FOREVER);
+
+    TEST_ASSERT(ble_gap_test_disc_event_type == BLE_GAP_EVENT_DISC_COMPLETE);
+
+    /* Clear GAP event for remainder of test. */
+    ble_gap_test_util_reset_cb_info();
+}
+
+TEST_CASE(ble_gap_test_case_conn_timeout_conn_forever)
+{
+    ble_gap_test_util_init();
+
+    /* 3 ms. */
+    ble_gap_test_util_conn_timeout(3);
+
+    /* No timeout. */
+    ble_gap_test_util_conn_forever();
+
+}
+
+TEST_CASE(ble_gap_test_case_conn_timeout_conn_timeout)
+{
+    ble_gap_test_util_init();
+
+    /* 30 ms. */
+    ble_gap_test_util_conn_timeout(30);
+
+    /* 5 ms. */
+    ble_gap_test_util_conn_timeout(5);
+
+}
+
+TEST_CASE(ble_gap_test_case_conn_forever_conn_timeout)
+{
+    ble_gap_test_util_init();
+
+    /* No timeout. */
+    ble_gap_test_util_conn_forever();
+
+    /* Cancel connect procedure manually. */
+    ble_gap_test_util_conn_cancel(0);
+
+    /* Clear GAP event for remainder of test. */
+    ble_gap_test_util_reset_cb_info();
+
+    /* 3 ms. */
+    ble_gap_test_util_conn_timeout(3);
+}
+
+TEST_CASE(ble_gap_test_case_disc_timeout_disc_forever)
+{
+    ble_gap_test_util_init();
+
+    /* 3 ms. */
+    ble_gap_test_util_disc_timeout(3);
+
+    /* No timeout. */
+    ble_gap_test_util_disc_forever();
+
+}
+
+TEST_CASE(ble_gap_test_case_disc_timeout_disc_timeout)
+{
+    ble_gap_test_util_init();
+
+    /* 30 ms. */
+    ble_gap_test_util_disc_timeout(30);
+
+    /* 5 ms. */
+    ble_gap_test_util_disc_timeout(5);
+
+}
+
+TEST_CASE(ble_gap_test_case_disc_forever_disc_timeout)
+{
+    ble_gap_test_util_init();
+
+    /* No timeout. */
+    ble_gap_test_util_disc_forever();
+
+    /* Cancel discovery procedure manually. */
+    ble_hs_test_util_disc_cancel(0);
+
+    /* 3 ms. */
+    ble_gap_test_util_disc_timeout(3);
+}
+
+TEST_CASE(ble_gap_test_case_conn_timeout_disc_timeout)
+{
+    ble_gap_test_util_init();
+
+    /* 15 seconds. */
+    ble_gap_test_util_conn_timeout(15000);
+
+    /* 1285 ms. */
+    ble_gap_test_util_disc_timeout(1285);
+}
+
+TEST_SUITE(ble_gap_test_suite_timeout)
+{
+    tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
+
+    ble_gap_test_case_conn_timeout_conn_forever();
+    ble_gap_test_case_conn_timeout_conn_timeout();
+    ble_gap_test_case_conn_forever_conn_timeout();
+
+    ble_gap_test_case_disc_timeout_disc_forever();
+    ble_gap_test_case_disc_timeout_disc_timeout();
+    ble_gap_test_case_disc_forever_disc_timeout();
+
+    ble_gap_test_case_conn_timeout_disc_timeout();
+}
+
+/*****************************************************************************
  * $all                                                                      *
  *****************************************************************************/
 
@@ -2295,6 +2478,7 @@ ble_gap_test_all(void)
     ble_gap_test_suite_adv();
     ble_gap_test_suite_stop_adv();
     ble_gap_test_suite_update_conn();
+    ble_gap_test_suite_timeout();
 
     return tu_any_failed;
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/db980b24/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 2fbe0ef..1a6dd5e 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.c
+++ b/net/nimble/host/src/test/ble_hs_test_util.c
@@ -408,6 +408,70 @@ ble_hs_test_util_create_conn(uint16_t handle, uint8_t *peer_id_addr,
                                      null_addr, cb, cb_arg);
 }
 
+static void
+ble_hs_test_util_conn_params_dflt(struct ble_gap_conn_params *conn_params)
+{
+    conn_params->scan_itvl = 0x0010;
+    conn_params->scan_window = 0x0010;
+    conn_params->itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN;
+    conn_params->itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX;
+    conn_params->latency = BLE_GAP_INITIAL_CONN_LATENCY;
+    conn_params->supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT;
+    conn_params->min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN;
+    conn_params->max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN;
+}
+
+static void
+ble_hs_test_util_hcc_from_conn_params(
+    struct hci_create_conn *hcc, uint8_t own_addr_type, uint8_t peer_addr_type,
+    const uint8_t *peer_addr, const struct ble_gap_conn_params *conn_params)
+{
+    hcc->scan_itvl = conn_params->scan_itvl;
+    hcc->scan_window = conn_params->scan_window;
+
+    if (peer_addr_type == BLE_GAP_ADDR_TYPE_WL) {
+        hcc->filter_policy = BLE_HCI_CONN_FILT_USE_WL;
+        hcc->peer_addr_type = 0;
+        memset(hcc->peer_addr, 0, 6);
+    } else {
+        hcc->filter_policy = BLE_HCI_CONN_FILT_NO_WL;
+        hcc->peer_addr_type = peer_addr_type;
+        memcpy(hcc->peer_addr, peer_addr, 6);
+    }
+    hcc->own_addr_type = own_addr_type;
+    hcc->conn_itvl_min = conn_params->itvl_min;
+    hcc->conn_itvl_max = conn_params->itvl_max;
+    hcc->conn_latency = conn_params->latency;
+    hcc->supervision_timeout = conn_params->supervision_timeout;
+    hcc->min_ce_len = conn_params->min_ce_len;
+    hcc->max_ce_len = conn_params->max_ce_len;
+}
+
+void
+ble_hs_test_util_verify_tx_create_conn(const struct hci_create_conn *exp)
+{
+    uint8_t param_len;
+    uint8_t *param;
+
+    param = ble_hs_test_util_verify_tx_hci(BLE_HCI_OGF_LE,
+                                           BLE_HCI_OCF_LE_CREATE_CONN,
+                                           &param_len);
+    TEST_ASSERT(param_len == BLE_HCI_CREATE_CONN_LEN);
+
+    TEST_ASSERT(le16toh(param + 0) == exp->scan_itvl);
+    TEST_ASSERT(le16toh(param + 2) == exp->scan_window);
+    TEST_ASSERT(param[4] == exp->filter_policy);
+    TEST_ASSERT(param[5] == exp->peer_addr_type);
+    TEST_ASSERT(memcmp(param + 6, exp->peer_addr, 6) == 0);
+    TEST_ASSERT(param[12] == exp->own_addr_type);
+    TEST_ASSERT(le16toh(param + 13) == exp->conn_itvl_min);
+    TEST_ASSERT(le16toh(param + 15) == exp->conn_itvl_max);
+    TEST_ASSERT(le16toh(param + 17) == exp->conn_latency);
+    TEST_ASSERT(le16toh(param + 19) == exp->supervision_timeout);
+    TEST_ASSERT(le16toh(param + 21) == exp->min_ce_len);
+    TEST_ASSERT(le16toh(param + 23) == exp->max_ce_len);
+}
+
 int
 ble_hs_test_util_connect(uint8_t own_addr_type, uint8_t peer_addr_type,
                          const uint8_t *peer_addr, int32_t duration_ms,
@@ -415,8 +479,16 @@ ble_hs_test_util_connect(uint8_t own_addr_type, uint8_t peer_addr_type,
                          ble_gap_event_fn *cb, void *cb_arg,
                          uint8_t ack_status)
 {
+    struct ble_gap_conn_params dflt_params;
+    struct hci_create_conn hcc;
     int rc;
 
+    /* This function ensures the most recently sent HCI command is the expected
+     * create connection command.  If the current test case has unverified HCI
+     * commands, assume we are not interested in them and clear the queue.
+     */
+    ble_hs_test_util_prev_hci_tx_clear();
+
     ble_hs_test_util_set_ack(
         host_hci_opcode_join(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CREATE_CONN),
         ack_status);
@@ -426,6 +498,15 @@ ble_hs_test_util_connect(uint8_t own_addr_type, uint8_t peer_addr_type,
 
     TEST_ASSERT(rc == BLE_HS_HCI_ERR(ack_status));
 
+    if (params == NULL) {
+        ble_hs_test_util_conn_params_dflt(&dflt_params);
+        params = &dflt_params;
+    }
+
+    ble_hs_test_util_hcc_from_conn_params(&hcc, own_addr_type,
+                                          peer_addr_type, peer_addr, params);
+    ble_hs_test_util_verify_tx_create_conn(&hcc);
+
     return rc;
 }
 
@@ -443,6 +524,23 @@ ble_hs_test_util_conn_cancel(uint8_t ack_status)
     return rc;
 }
 
+void
+ble_hs_test_util_conn_cancel_full(void)
+{
+    struct hci_le_conn_complete evt;
+    int rc;
+
+    ble_hs_test_util_conn_cancel(0);
+
+    memset(&evt, 0, sizeof evt);
+    evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE;
+    evt.status = BLE_ERR_UNK_CONN_ID;
+    evt.role = BLE_HCI_LE_CONN_COMPLETE_ROLE_MASTER;
+
+    rc = ble_gap_rx_conn_complete(&evt);
+    TEST_ASSERT_FATAL(rc == 0);
+}
+
 int
 ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status)
 {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/db980b24/net/nimble/host/src/test/ble_hs_test_util.h
----------------------------------------------------------------------
diff --git a/net/nimble/host/src/test/ble_hs_test_util.h b/net/nimble/host/src/test/ble_hs_test_util.h
index b663ce2..9c4b982 100644
--- a/net/nimble/host/src/test/ble_hs_test_util.h
+++ b/net/nimble/host/src/test/ble_hs_test_util.h
@@ -27,6 +27,7 @@
 struct ble_hs_conn;
 struct ble_l2cap_chan;
 struct hci_disconn_complete;
+struct hci_create_conn;
 
 extern struct os_eventq ble_hs_test_util_evq;
 extern const struct ble_gap_adv_params ble_hs_test_util_adv_params;
@@ -79,6 +80,7 @@ int ble_hs_test_util_connect(uint8_t own_addr_type,
                                    void *cb_arg,
                                    uint8_t ack_status);
 int ble_hs_test_util_conn_cancel(uint8_t ack_status);
+void ble_hs_test_util_conn_cancel_full(void);
 int ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status);
 void ble_hs_test_util_conn_disconnect(uint16_t conn_handle);
 int ble_hs_test_util_exp_hci_status(int cmd_idx, int fail_idx,
@@ -88,6 +90,7 @@ int ble_hs_test_util_disc(uint8_t own_addr_type, int32_t duration_ms,
                           ble_gap_event_fn *cb, void *cb_arg, int fail_idx,
                           uint8_t fail_status);
 int ble_hs_test_util_disc_cancel(uint8_t ack_status);
+void ble_hs_test_util_verify_tx_create_conn(const struct hci_create_conn *exp);
 int ble_hs_test_util_adv_set_fields(struct ble_hs_adv_fields *adv_fields,
                                     uint8_t hci_status);
 int ble_hs_test_util_adv_start(uint8_t own_addr_type,