ccollins476ad closed pull request #596: MYNEWT-842  BLE Host - Handle failure 
to add entry to resolving list
URL: https://github.com/apache/mynewt-core/pull/596
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/net/nimble/host/include/host/ble_gap.h 
b/net/nimble/host/include/host/ble_gap.h
index 111c20742..8896f2d17 100644
--- a/net/nimble/host/include/host/ble_gap.h
+++ b/net/nimble/host/include/host/ble_gap.h
@@ -346,6 +346,39 @@ struct ble_gap_event {
          */
         struct ble_gap_ext_disc_desc ext_disc;
 #endif
+
+        /**
+         * Represents a completed discovery procedure.  Valid for the following
+         * event types:
+         *     o BLE_GAP_EVENT_DISC_COMPLETE
+         */
+        struct {
+            /**
+             * The reason the discovery procedure stopped.  Typical reason
+             * codes are:
+             *     o 0: Duration expired.
+             *     o BLE_HS_EPREEMPTED: Host aborted procedure to configure a
+             *       peer's identity.
+             */
+            int reason;
+        } disc_complete;
+
+        /**
+         * Represents a completed advertise procedure.  Valid for the following
+         * event types:
+         *     o BLE_GAP_EVENT_ADV_COMPLETE
+         */
+        struct {
+            /**
+             * The reason the advertise procedure stopped.  Typical reason
+             * codes are:
+             *     o 0: Duration expired.
+             *     o BLE_HS_EPREEMPTED: Host aborted procedure to configure a
+             *       peer's identity.
+             */
+            int reason;
+        } adv_complete;
+
         /**
          * Represents an attempt to update a connection's parameters.  If the
          * attempt was successful, the connection's descriptor reflects the
diff --git a/net/nimble/host/include/host/ble_hs.h 
b/net/nimble/host/include/host/ble_hs.h
index 85885bd4a..da0468bcb 100644
--- a/net/nimble/host/include/host/ble_hs.h
+++ b/net/nimble/host/include/host/ble_hs.h
@@ -76,6 +76,7 @@ struct os_event;
 #define BLE_HS_EENCRYPT_KEY_SZ      26
 #define BLE_HS_ESTORE_CAP           27
 #define BLE_HS_ESTORE_FAIL          28
+#define BLE_HS_EPREEMPTED           29
 
 #define BLE_HS_ERR_ATT_BASE         0x100   /* 256 */
 #define BLE_HS_ATT_ERR(x)           ((x) ? BLE_HS_ERR_ATT_BASE + (x) : 0)
diff --git a/net/nimble/host/src/ble_gap.c b/net/nimble/host/src/ble_gap.c
index 01a2ab2e0..e870cd922 100644
--- a/net/nimble/host/src/ble_gap.c
+++ b/net/nimble/host/src/ble_gap.c
@@ -44,6 +44,16 @@
  * For (2), the procedure result is indicated by an application-configured
  * callback.  The callback is executed when the procedure completes.
  *
+ * The GAP is always in one of two states:
+ * 1. Free
+ * 2. Preempted
+ *
+ * While GAP is in the free state, new procedures can be started at will.
+ * While GAP is in the preempted state, no new procedures are allowed.  The
+ * host sets GAP to the preempted state when it needs to ensure no ongoing
+ * procedures, a condition required for some HCI commands to succeed.  The host
+ * must take care to take GAP out of the preempted state as soon as possible.
+ *
  * Notes on thread-safety:
  * 1. The ble_hs mutex must always be unlocked when an application callback is
  *    executed.  The purpose of this requirement is to allow callbacks to
@@ -100,8 +110,13 @@ struct ble_gap_master_state {
     ble_gap_event_fn *cb;
     void *cb_arg;
 
-    union {
+    /**
+     * Indicates the type of master procedure that was preempted, or
+     * BLE_GAP_OP_NULL if no procedure was preempted.
+     */
+    uint8_t preempted_op;
 
+    union {
         struct {
             uint8_t using_wl:1;
             uint8_t our_addr_type:2;
@@ -113,12 +128,9 @@ struct ble_gap_master_state {
             uint8_t extended:1;
         } disc;
     };
-
-
 };
 static bssnz_t struct ble_gap_master_state ble_gap_master;
 
-
 #if MYNEWT_VAL(BLE_MESH)
 struct ble_gap_mesh_state {
     ble_gap_event_fn *cb;
@@ -141,6 +153,9 @@ static bssnz_t struct {
     unsigned our_addr_type:2;
     ble_gap_event_fn *cb;
     void *cb_arg;
+
+    /** Set to 1 if advertising was preempted. */
+    uint8_t preempted:1;
 } ble_gap_slave;
 
 struct ble_gap_update_entry {
@@ -314,7 +329,8 @@ ble_gap_log_adv(uint8_t own_addr_type, const ble_addr_t 
*direct_addr,
 {
     BLE_HS_LOG(INFO, "disc_mode=%d", adv_params->disc_mode);
     if (direct_addr) {
-        BLE_HS_LOG(INFO, " direct_addr_type=%d direct_addr=", 
direct_addr->type);
+        BLE_HS_LOG(INFO, " direct_addr_type=%d direct_addr=",
+                   direct_addr->type);
         BLE_HS_LOG_ADDR(INFO, direct_addr->val);
     }
     BLE_HS_LOG(INFO, " adv_channel_map=%d own_addr_type=%d "
@@ -501,8 +517,8 @@ ble_gap_set_prefered_default_le_phy(uint8_t tx_phys_mask, 
uint8_t rx_phys_mask)
     uint8_t buf[BLE_HCI_LE_SET_DEFAULT_PHY_LEN];
     int rc;
 
-    rc = ble_hs_hci_cmd_build_le_set_default_phy(tx_phys_mask, rx_phys_mask, 
buf,
-                                             sizeof(buf));
+    rc = ble_hs_hci_cmd_build_le_set_default_phy(tx_phys_mask, rx_phys_mask,
+                                                 buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -528,14 +544,16 @@ ble_gap_set_prefered_le_phy(uint16_t conn_handle, uint8_t 
tx_phys_mask,
         return BLE_HS_ENOTCONN;
     }
 
-    rc = ble_hs_hci_cmd_build_le_set_phy(conn_handle, tx_phys_mask, 
rx_phys_mask,
-                                     phy_opts, buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_build_le_set_phy(conn_handle, tx_phys_mask,
+                                         rx_phys_mask, phy_opts, buf,
+                                         sizeof(buf));
     if (rc != 0) {
         return rc;
     }
 
-    return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, 
BLE_HCI_OCF_LE_SET_PHY),
-                             buf, sizeof(buf), NULL, 0, NULL);
+    return ble_hs_hci_cmd_tx(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PHY),
+        buf, sizeof(buf), NULL, 0, NULL);
 }
 
 #if MYNEWT_VAL(BLE_MESH)
@@ -596,6 +614,15 @@ ble_gap_call_conn_event_cb(struct ble_gap_event *event, 
uint16_t conn_handle)
     return 0;
 }
 
+static bool
+ble_gap_is_preempted(void)
+{
+    BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
+
+    return ble_gap_slave.preempted ||
+           ble_gap_master.preempted_op != BLE_GAP_OP_NULL;
+}
+
 static void
 ble_gap_master_reset_state(void)
 {
@@ -635,6 +662,7 @@ ble_gap_master_extract_state(struct ble_gap_master_state 
*out_state,
 
     if (reset_state) {
         ble_gap_master_reset_state();
+        ble_gap_master.preempted_op = BLE_GAP_OP_NULL;
     }
 
     ble_hs_unlock();
@@ -663,7 +691,7 @@ ble_gap_adv_finished(void)
     if (cb != NULL) {
         memset(&event, 0, sizeof event);
         event.type = BLE_GAP_EVENT_ADV_COMPLETE;
-
+        event.adv_complete.reason = 0;
         cb(&event, cb_arg);
     }
 }
@@ -724,7 +752,7 @@ ble_gap_is_extended_disc(void)
 static void
 ble_gap_disc_report(void *desc)
 {
-    struct ble_gap_master_state state;;
+    struct ble_gap_master_state state;
     struct ble_gap_event event;
 
     memset(&event, 0, sizeof event);
@@ -761,6 +789,7 @@ ble_gap_disc_complete(void)
 
     memset(&event, 0, sizeof event);
     event.type = BLE_GAP_EVENT_DISC_COMPLETE;
+    event.disc_complete.reason = 0;
 
     ble_gap_master_extract_state(&state, 1);
     if (ble_gap_has_client(&state)) {
@@ -1313,7 +1342,11 @@ 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_cancelled();
+                    if (ble_gap_master.preempted_op == BLE_GAP_OP_M_CONN) {
+                        ble_gap_master_failed(BLE_HS_EPREEMPTED);
+                    } else {
+                        ble_gap_master_connect_cancelled();
+                    }
                 } else {
                     ble_gap_master_failed(BLE_HS_HCI_ERR(evt->status));
                 }
@@ -1409,7 +1442,8 @@ ble_gap_rx_conn_complete(struct hci_le_conn_complete *evt)
 }
 
 void
-ble_gap_rx_rd_rem_sup_feat_complete(struct hci_le_rd_rem_supp_feat_complete 
*evt)
+ble_gap_rx_rd_rem_sup_feat_complete(
+    struct hci_le_rd_rem_supp_feat_complete *evt)
 {
 #if !NIMBLE_BLE_CONNECT
     return;
@@ -1674,9 +1708,9 @@ ble_gap_wl_tx_clear(void)
 {
     int rc;
 
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                               
BLE_HCI_OCF_LE_CLEAR_WHITE_LIST),
-                                     NULL, 0);
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_CLEAR_WHITE_LIST),
+        NULL, 0);
     if (rc != 0) {
         return rc;
     }
@@ -1794,18 +1828,8 @@ ble_gap_adv_enable_tx(int enable, bool directed)
     return 0;
 }
 
-/**
- * Stops the currently-active advertising procedure.  A success return
- * code indicates that advertising has been fully aborted; a new advertising
- * procedure can be initiated immediately.
- *
- * @return                      0 on success;
- *                              BLE_HS_EALREADY if there is no active
- *                                  advertising procedure;
- *                              Other nonzero on error.
- */
-int
-ble_gap_adv_stop(void)
+static int
+ble_gap_adv_stop_no_lock(void)
 {
 #if !NIMBLE_BLE_ADVERTISE
     return BLE_HS_ENOTSUP;
@@ -1813,9 +1837,9 @@ ble_gap_adv_stop(void)
 
     int rc;
 
-    STATS_INC(ble_gap_stats, adv_stop);
+    BLE_HS_DBG_ASSERT(ble_hs_locked_by_cur_task());
 
-    ble_hs_lock();
+    STATS_INC(ble_gap_stats, adv_stop);
 
     /* Do nothing if advertising is already disabled. */
     if (!ble_gap_adv_active()) {
@@ -1835,15 +1859,39 @@ ble_gap_adv_stop(void)
     rc = 0;
 
 done:
-    ble_hs_unlock();
-
     if (rc != 0) {
-        STATS_INC(ble_gap_stats, adv_set_data_fail);
+        STATS_INC(ble_gap_stats, adv_stop_fail);
     }
 
     return rc;
 }
 
+/**
+ * Stops the currently-active advertising procedure.  A success return
+ * code indicates that advertising has been fully aborted; a new advertising
+ * procedure can be initiated immediately.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_EALREADY if there is no active
+ *                                  advertising procedure;
+ *                              Other nonzero on error.
+ */
+int
+ble_gap_adv_stop(void)
+{
+#if !NIMBLE_BLE_ADVERTISE
+    return BLE_HS_ENOTSUP;
+#endif
+
+    int rc;
+
+    ble_hs_lock();
+    rc = ble_gap_adv_stop_no_lock();
+    ble_hs_unlock();
+
+    return rc;
+}
+
 /*****************************************************************************
  * $advertise                                                                *
  *****************************************************************************/
@@ -2033,9 +2081,9 @@ ble_gap_adv_params_tx(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
         return BLE_HS_EINVAL;
     }
 
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                              
BLE_HCI_OCF_LE_SET_EXT_ADV_PARAM),
-                                     buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_EXT_ADV_PARAM),
+        buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -2170,8 +2218,8 @@ ble_gap_adv_validate(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
  *                                      o BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
  *                                      o BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
  * @param direct_addr           The peer's address for directed advertising.
- *                                  This parameter shall be non-NULL if 
directed
- *                                  advertising is being used.
+ *                                  This parameter shall be non-NULL if
+ *                                  directed advertising is being used.
  * @param duration_ms           The duration of the advertisement procedure.
  *                                  On expiration, the procedure ends and a
  *                                  BLE_GAP_EVENT_ADV_COMPLETE event is
@@ -2221,6 +2269,11 @@ ble_gap_adv_start(uint8_t own_addr_type, const 
ble_addr_t *direct_addr,
         }
     }
 
+    if (ble_gap_is_preempted()) {
+        rc = BLE_HS_EPREEMPTED;
+        goto done;
+    }
+
     rc = ble_hs_id_use_addr(own_addr_type);
     if (rc != 0) {
         goto done;
@@ -2296,7 +2349,8 @@ ble_gap_adv_set_data(const uint8_t *data, int data_len)
                                     0, data, data_len, buf, sizeof(buf));
 #else
     opcode = BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_ADV_DATA);
-    rc = ble_hs_hci_cmd_build_le_set_adv_data(data, data_len, buf, 
sizeof(buf));
+    rc = ble_hs_hci_cmd_build_le_set_adv_data(data, data_len, buf,
+                                              sizeof(buf));
 #endif
     if (rc != 0) {
         goto done;
@@ -2448,7 +2502,8 @@ ble_gap_adv_active(void)
 }
 
 #if MYNEWT_VAL(BLE_EXT_ADV)
-int ble_gap_adv_set_tx_power(int8_t tx_power)
+int
+ble_gap_adv_set_tx_power(int8_t tx_power)
 {
     ble_hs_lock();
 
@@ -2464,7 +2519,8 @@ int ble_gap_adv_set_tx_power(int8_t tx_power)
     return 0;
 }
 
-int ble_gap_adv_set_phys(uint8_t primary_phy, uint8_t secondary_phy)
+int
+ble_gap_adv_set_phys(uint8_t primary_phy, uint8_t secondary_phy)
 {
     if (primary_phy) {
         /* primary cannot be 2M */
@@ -2473,7 +2529,9 @@ int ble_gap_adv_set_phys(uint8_t primary_phy, uint8_t 
secondary_phy)
             return BLE_HS_EINVAL;
         }
 
-        /* if primary is not legacy then secondary must not be legacy as well 
*/
+        /* if primary is not legacy then secondary must not be legacy as
+         * well
+         */
         if (!secondary_phy || secondary_phy > BLE_HCI_LE_PHY_CODED) {
             return BLE_HS_EINVAL;
         }
@@ -2513,9 +2571,9 @@ ble_gap_disc_enable_tx(int enable, int filter_duplicates)
 
     ble_hs_hci_cmd_build_le_set_scan_enable(!!enable, !!filter_duplicates,
                                             buf, sizeof buf);
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                                
BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
-                                     buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
+        buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -2547,9 +2605,9 @@ ble_gap_disc_tx_params(uint8_t own_addr_type,
         return BLE_HS_EINVAL;
     }
 
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                                
BLE_HCI_OCF_LE_SET_SCAN_PARAMS),
-                                     buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_SCAN_PARAMS),
+        buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -2613,24 +2671,14 @@ ble_gap_ext_disc_enable_tx(uint8_t enable, uint8_t 
filter_duplicates,
                                                 duration, period,
                                                 buf, sizeof buf);
 
-    return ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                            
BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE),
-                                       buf, sizeof(buf));
+    return ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_EXT_SCAN_ENABLE),
+        buf, sizeof(buf));
 }
 #endif
 
-/**
- * Cancels the discovery procedure currently in progress.  A success return
- * code indicates that scanning has been fully aborted; a new discovery or
- * connect procedure can be initiated immediately.
- *
- * @return                      0 on success;
- *                              BLE_HS_EALREADY if there is no discovery
- *                                  procedure to cancel;
- *                              Other nonzero on unexpected error.
- */
-int
-ble_gap_disc_cancel(void)
+static int
+ble_gap_disc_disable_tx(void)
 {
 #if !NIMBLE_BLE_SCAN
     return BLE_HS_ENOTSUP;
@@ -2638,15 +2686,6 @@ ble_gap_disc_cancel(void)
 
     int rc;
 
-    STATS_INC(ble_gap_stats, discover_cancel);
-
-    ble_hs_lock();
-
-    if (!ble_gap_disc_active()) {
-        rc = BLE_HS_EALREADY;
-        goto done;
-    }
-
     if (!ble_gap_is_extended_disc()) {
         rc = ble_gap_disc_enable_tx(0, 0);
     } else {
@@ -2654,8 +2693,30 @@ ble_gap_disc_cancel(void)
         rc = ble_gap_ext_disc_enable_tx(0, 0, 0, 0);
 #else
         assert(0);
+        rc = BLE_HS_EUNKNOWN;
 #endif
     }
+
+    return rc;
+}
+
+static int
+ble_gap_disc_cancel_no_lock(void)
+{
+#if !NIMBLE_BLE_SCAN
+    return BLE_HS_ENOTSUP;
+#endif
+
+    int rc;
+
+    STATS_INC(ble_gap_stats, discover_cancel);
+
+    if (!ble_gap_disc_active()) {
+        rc = BLE_HS_EALREADY;
+        goto done;
+    }
+
+    rc = ble_gap_disc_disable_tx();
     if (rc != 0) {
         goto done;
     }
@@ -2663,8 +2724,6 @@ ble_gap_disc_cancel(void)
     ble_gap_master_reset_state();
 
 done:
-    ble_hs_unlock();
-
     if (rc != 0) {
         STATS_INC(ble_gap_stats, discover_cancel_fail);
     }
@@ -2673,6 +2732,32 @@ ble_gap_disc_cancel(void)
 #endif
 }
 
+/**
+ * Cancels the discovery procedure currently in progress.  A success return
+ * code indicates that scanning has been fully aborted; a new discovery or
+ * connect procedure can be initiated immediately.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_EALREADY if there is no discovery
+ *                                  procedure to cancel;
+ *                              Other nonzero on unexpected error.
+ */
+int
+ble_gap_disc_cancel(void)
+{
+#if !MYNEWT_VAL(BLE_ROLE_OBSERVER)
+    return BLE_HS_ENOTSUP;
+#endif
+
+    int rc;
+
+    ble_hs_lock();
+    rc = ble_gap_disc_cancel_no_lock();
+    ble_hs_unlock();
+
+    return rc;
+}
+
 #if NIMBLE_BLE_SCAN
 static int
 ble_gap_disc_ext_validate(uint8_t own_addr_type)
@@ -2689,6 +2774,10 @@ ble_gap_disc_ext_validate(uint8_t own_addr_type)
         return BLE_HS_EALREADY;
     }
 
+    if (ble_gap_is_preempted()) {
+        return BLE_HS_EPREEMPTED;
+    }
+
     return 0;
 }
 #endif
@@ -3048,11 +3137,11 @@ ble_gap_copy_params(struct hci_ext_conn_params 
*hcc_params,
 }
 
 static int
-ble_gap_ext_conn_create_tx(uint8_t own_addr_type, const ble_addr_t *peer_addr,
-                           uint8_t phy_mask,
-                           const struct ble_gap_conn_params 
*phy_1m_conn_params,
-                           const struct ble_gap_conn_params 
*phy_2m_conn_params,
-                           const struct ble_gap_conn_params 
*phy_coded_conn_params)
+ble_gap_ext_conn_create_tx(
+    uint8_t own_addr_type, const ble_addr_t *peer_addr, uint8_t phy_mask,
+    const struct ble_gap_conn_params *phy_1m_conn_params,
+    const struct ble_gap_conn_params *phy_2m_conn_params,
+    const struct ble_gap_conn_params *phy_coded_conn_params)
 {
     uint8_t buf[sizeof(struct hci_ext_create_conn)];
     struct hci_ext_create_conn hcc = {0};
@@ -3096,9 +3185,9 @@ ble_gap_ext_conn_create_tx(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
         return BLE_HS_EUNKNOWN;
     }
 
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                                
BLE_HCI_OCF_LE_EXT_CREATE_CONN),
-                                     buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_EXT_CREATE_CONN),
+        buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -3124,21 +3213,21 @@ ble_gap_ext_conn_create_tx(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
  *                                  reported.  Units are milliseconds.
  * @param phy_mask              Define on which PHYs connection attempt should
  *                                  be done
- * @param phy_1m_conn_params     Additional arguments specifying the 
particulars
- *                                  of the connect procedure. When
+ * @param phy_1m_conn_params     Additional arguments specifying the
+ *                                  particulars of the connect procedure. When
  *                                  BLE_GAP_LE_PHY_1M_MASK is set in phy_mask
  *                                  this parameter can be specify to null for
  *                                  default values.
- * @param phy_2m_conn_params     Additional arguments specifying the 
particulars
- *                                  of the connect procedure. When
+ * @param phy_2m_conn_params     Additional arguments specifying the
+ *                                  particulars of the connect procedure. When
  *                                  BLE_GAP_LE_PHY_2M_MASK is set in phy_mask
  *                                  this parameter can be specify to null for
  *                                  default values.
- * @param phy_coded_conn_params  Additional arguments specifying the 
particulars
- *                                  of the connect procedure. When
- *                                  BLE_GAP_LE_PHY_CODED_MASK is set in 
phy_mask
- *                                  this parameter can be specify to null for
- *                                  default values.
+ * @param phy_coded_conn_params  Additional arguments specifying the
+ *                                  particulars of the connect procedure. When
+ *                                  BLE_GAP_LE_PHY_CODED_MASK is set in
+ *                                  phy_mask this parameter can be specify to
+ *                                  null for default values.
  * @param cb                    The callback to associate with this connect
  *                                  procedure.  When the connect procedure
  *                                  completes, the result is reported through
@@ -3186,6 +3275,11 @@ ble_gap_ext_connect(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
         goto done;
     }
 
+    if (ble_gap_is_preempted()) {
+        rc = BLE_HS_EPREEMPTED;
+        goto done;
+    }
+
     if (!ble_hs_conn_can_alloc()) {
         rc = BLE_HS_ENOMEM;
         goto done;
@@ -3209,7 +3303,9 @@ ble_gap_ext_connect(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
         phy_2m_conn_params = &ble_gap_conn_params_dflt;
     }
 
-    if ((phy_mask & BLE_GAP_LE_PHY_CODED_MASK) && phy_coded_conn_params == 
NULL) {
+    if ((phy_mask & BLE_GAP_LE_PHY_CODED_MASK) &&
+        phy_coded_conn_params == NULL) {
+
         phy_coded_conn_params = &ble_gap_conn_params_dflt;
     }
 
@@ -3334,6 +3430,11 @@ ble_gap_connect(uint8_t own_addr_type, const ble_addr_t 
*peer_addr,
         goto done;
     }
 
+    if (ble_gap_is_preempted()) {
+        rc = BLE_HS_EPREEMPTED;
+        goto done;
+    }
+
     if (!ble_hs_conn_can_alloc()) {
         rc = BLE_HS_ENOMEM;
         goto done;
@@ -3508,23 +3609,17 @@ ble_gap_conn_cancel_tx(void)
     return 0;
 }
 
-/**
- * Aborts a connect procedure in progress.
- *
- * @return                      0 on success;
- *                              BLE_HS_EALREADY if there is no active connect
- *                                  procedure.
- *                              Other nonzero on error.
- */
-int
-ble_gap_conn_cancel(void)
+static int
+ble_gap_conn_cancel_no_lock(void)
 {
+#if !MYNEWT_VAL(BLE_ROLE_CENTRAL)
+    return BLE_HS_ENOTSUP;
+#endif
+
     int rc;
 
     STATS_INC(ble_gap_stats, cancel);
 
-    ble_hs_lock();
-
     if (!ble_gap_conn_active()) {
         rc = BLE_HS_EALREADY;
         goto done;
@@ -3541,11 +3636,34 @@ ble_gap_conn_cancel(void)
     rc = 0;
 
 done:
-    ble_hs_unlock();
-
     if (rc != 0) {
         STATS_INC(ble_gap_stats, cancel_fail);
     }
+
+    return rc;
+}
+
+/**
+ * Aborts a connect procedure in progress.
+ *
+ * @return                      0 on success;
+ *                              BLE_HS_EALREADY if there is no active connect
+ *                                  procedure.
+ *                              Other nonzero on error.
+ */
+int
+ble_gap_conn_cancel(void)
+{
+#if !MYNEWT_VAL(BLE_ROLE_CENTRAL)
+    return BLE_HS_ENOTSUP;
+#endif
+
+    int rc;
+
+    ble_hs_lock();
+    rc = ble_gap_conn_cancel_no_lock();
+    ble_hs_unlock();
+
     return rc;
 }
 
@@ -3661,9 +3779,9 @@ ble_gap_tx_param_pos_reply(uint16_t conn_handle,
     pos_reply.max_ce_len = params->max_ce_len;
 
     ble_hs_hci_cmd_build_le_conn_param_reply(&pos_reply, buf, sizeof buf);
-    rc = ble_hs_hci_cmd_tx_empty_ack(BLE_HCI_OP(BLE_HCI_OGF_LE,
-                                              
BLE_HCI_OCF_LE_REM_CONN_PARAM_RR),
-                                     buf, sizeof(buf));
+    rc = ble_hs_hci_cmd_tx_empty_ack(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_REM_CONN_PARAM_RR),
+        buf, sizeof(buf));
     if (rc != 0) {
         return rc;
     }
@@ -3817,7 +3935,8 @@ ble_gap_validate_conn_params(const struct 
ble_gap_upd_params *params)
  *                              BLE_HS_EALREADY if a connection update
  *                                  procedure for this connection is already in
  *                                  progress;
- *                              BLE_HS_EINVAL if requested parameters are 
invalid;
+ *                              BLE_HS_EINVAL if requested parameters are
+ *                                  invalid;
  *                              Other nonzero on error.
  */
 int
@@ -3876,8 +3995,8 @@ ble_gap_update_params(uint16_t conn_handle,
     BLE_HS_LOG(INFO, "\n");
 
     /*
-     * If LL update procedure is not supported on this connection and we are 
the
-     * slave, fail over to the L2CAP update procedure.
+     * If LL update procedure is not supported on this connection and we are
+     * the slave, fail over to the L2CAP update procedure.
      */
     if ((conn->supported_feat & BLE_HS_HCI_LE_FEAT_CONN_PARAM_REQUEST) == 0 &&
             !(conn->bhc_flags & BLE_HS_CONN_F_MASTER)) {
@@ -4231,6 +4350,97 @@ ble_gap_mtu_event(uint16_t conn_handle, uint16_t cid, 
uint16_t mtu)
 }
 
 /*****************************************************************************
+ * $preempt                                                                  *
+ *****************************************************************************/
+
+/**
+ * Aborts all active GAP procedures and prevents new ones from being started.
+ * This function is used to ensure an idle GAP so that the controller's
+ * resolving list can be modified.  When done accessing the resolving list, the
+ * caller must call `ble_gap_preempt_done()` to permit new GAP procedures.
+ *
+ * On preemption, all aborted GAP procedures are reported with a status or
+ * reason code of BLE_HS_EPREEMPTED.  An attempt to initiate a new GAP
+ * procedure during preemption fails with a return code of BLE_HS_EPREEMPTED.
+ */
+void
+ble_gap_preempt(void)
+{
+    int rc;
+
+    ble_hs_lock();
+
+    BLE_HS_DBG_ASSERT(!ble_gap_is_preempted());
+
+    rc = ble_gap_adv_stop_no_lock();
+    if (rc == 0) {
+        ble_gap_slave.preempted = 1;
+    }
+
+    rc = ble_gap_conn_cancel_no_lock();
+    if (rc == 0) {
+        ble_gap_master.preempted_op = BLE_GAP_OP_M_CONN;
+    }
+
+    rc = ble_gap_disc_cancel_no_lock();
+    if (rc == 0) {
+        ble_gap_master.preempted_op = BLE_GAP_OP_M_DISC;
+    }
+
+    ble_hs_unlock();
+}
+
+/**
+ * Takes GAP out of the preempted state, allowing new GAP procedures to be
+ * initiaited.  This function should only be called after a call to
+ * `ble_gap_preempt()`.
+ */
+void
+ble_gap_preempt_done(void)
+{
+    struct ble_gap_event event;
+    ble_gap_event_fn *master_cb;
+    ble_gap_event_fn *slave_cb;
+    void *master_arg;
+    void *slave_arg;
+    int disc_preempted;
+    int adv_preempted;
+
+    adv_preempted = 0;
+    disc_preempted = 0;
+
+    ble_hs_lock();
+
+    if (ble_gap_slave.preempted) {
+        ble_gap_slave.preempted = 0;
+        adv_preempted = 1;
+        slave_cb = ble_gap_slave.cb;
+        slave_arg = ble_gap_slave.cb_arg;
+    }
+
+    if (ble_gap_master.preempted_op == BLE_GAP_OP_M_DISC) {
+        ble_gap_master.preempted_op = BLE_GAP_OP_NULL;
+        disc_preempted = 1;
+        master_cb = ble_gap_master.cb;
+        master_arg = ble_gap_master.cb_arg;
+    }
+
+    ble_hs_unlock();
+
+    if (adv_preempted) {
+        event.type = BLE_GAP_EVENT_ADV_COMPLETE;
+        event.adv_complete.reason = BLE_HS_EPREEMPTED;
+        ble_gap_call_event_cb(&event, slave_cb, slave_arg);
+    }
+
+    if (disc_preempted) {
+        event.type = BLE_GAP_EVENT_DISC_COMPLETE;
+        event.disc_complete.reason = BLE_HS_EPREEMPTED;
+        ble_gap_call_event_cb(&event, master_cb, master_arg);
+    }
+}
+
+/*****************************************************************************
  * $init                                                                     *
  *****************************************************************************/
 
diff --git a/net/nimble/host/src/ble_gap_priv.h 
b/net/nimble/host/src/ble_gap_priv.h
index 88e62ca29..f90c1a01d 100644
--- a/net/nimble/host/src/ble_gap_priv.h
+++ b/net/nimble/host/src/ble_gap_priv.h
@@ -104,6 +104,9 @@ void ble_gap_identity_event(uint16_t conn_handle);
 int ble_gap_repeat_pairing_event(const struct ble_gap_repeat_pairing *rp);
 int ble_gap_master_in_progress(void);
 
+void ble_gap_preempt(void);
+void ble_gap_preempt_done(void);
+
 void ble_gap_conn_broken(uint16_t conn_handle, int reason);
 int32_t ble_gap_timer(void);
 
diff --git a/net/nimble/host/src/ble_hs.c b/net/nimble/host/src/ble_hs.c
index 33e51d0f1..c7755f364 100644
--- a/net/nimble/host/src/ble_hs.c
+++ b/net/nimble/host/src/ble_hs.c
@@ -631,6 +631,7 @@ ble_hs_init(void)
     /* These get initialized here to allow unit tests to run without a zeroed
      * bss.
      */
+    ble_hs_reset_reason = 0;
     ble_hs_ev_tx_notifications = (struct os_event) {
         .ev_cb = ble_hs_event_tx_notify,
     };
diff --git a/net/nimble/host/src/ble_hs_pvcy.c 
b/net/nimble/host/src/ble_hs_pvcy.c
index 95b22c262..dbabb720c 100644
--- a/net/nimble/host/src/ble_hs_pvcy.c
+++ b/net/nimble/host/src/ble_hs_pvcy.c
@@ -106,16 +106,15 @@ ble_hs_pvcy_clear_entries(void)
     return 0;
 }
 
-int
-ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type,
-                      const uint8_t *irk)
+static int
+ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type,
+                          const uint8_t *irk)
 {
     struct hci_add_dev_to_resolving_list add;
     uint8_t buf[BLE_HCI_ADD_TO_RESOLV_LIST_LEN];
+    ble_addr_t peer_addr;
     int rc;
 
-    STATS_INC(ble_hs_stats, pvcy_add_entry);
-
     add.addr_type = addr_type;
     memcpy(add.addr, addr, 6);
     memcpy(add.local_irk, ble_hs_pvcy_irk, 16);
@@ -123,20 +122,56 @@ ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t 
addr_type,
 
     rc = ble_hs_hci_cmd_build_add_to_resolv_list(&add, buf, sizeof(buf));
     if (rc != 0) {
-        goto err;
+        return rc;
     }
 
     rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
                                       BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
                            buf, sizeof(buf), NULL, 0, NULL);
     if (rc != 0) {
-        goto err;
+        return rc;
+    }
+
+
+    /* FIXME Controller is BT5.0 and default privacy mode is network which
+     * can cause problems for apps which are not aware of it. We need to
+     * sort it out somehow. For now we set device mode for all of the peer
+     * devices and application should change it to network if needed
+     */
+    peer_addr.type = addr_type;
+    memcpy(peer_addr.val, addr, sizeof peer_addr.val);
+    rc = ble_hs_pvcy_set_mode(&peer_addr, BLE_GAP_PRIVATE_MODE_DEVICE);
+    if (rc != 0) {
+        return rc;
     }
 
     return 0;
+}
+
+int
+ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type,
+                      const uint8_t *irk)
+{
+    int rc;
+
+    STATS_INC(ble_hs_stats, pvcy_add_entry);
+
+    /* No GAP procedures can be active when adding an entry to the resolving
+     * list (Vol 2, Part E, 7.8.38).  Stop all GAP procedures and temporarily
+     * prevent any new ones from being started.
+     */
+    ble_gap_preempt();
+
+    /* Try to add the entry now that GAP is halted. */
+    rc = ble_hs_pvcy_add_entry_hci(addr, addr_type, irk);
+
+    /* Allow GAP procedures to be started again. */
+    ble_gap_preempt_done();
+
+    if (rc != 0) {
+        STATS_INC(ble_hs_stats, pvcy_add_entry_fail);
+    }
 
-err:
-    STATS_INC(ble_hs_stats, pvcy_add_entry_fail);
     return rc;
 }
 
diff --git a/net/nimble/host/src/ble_store.c b/net/nimble/host/src/ble_store.c
index 0aa444ef2..22e608947 100644
--- a/net/nimble/host/src/ble_store.c
+++ b/net/nimble/host/src/ble_store.c
@@ -244,17 +244,6 @@ ble_store_write_peer_sec(const struct ble_store_value_sec 
*value_sec)
         if (rc != 0) {
             return rc;
         }
-
-        /* FIXME Controller is BT5.0 and default privacy mode is network which
-         * can cause problems for apps which are not aware of it. We need to
-         * sort it out somehow. For now we set device mode for all of the peer
-         * devices and application should change it to network if needed
-         */
-        rc = ble_hs_pvcy_set_mode(&value_sec->peer_addr,
-                                  BLE_GAP_PRIVATE_MODE_DEVICE);
-        if (rc != 0) {
-            return rc;
-        }
     }
 
     return 0;
diff --git a/net/nimble/host/test/src/ble_hs_pvcy_test.c 
b/net/nimble/host/test/src/ble_hs_pvcy_test.c
index 94fe90d09..67b766edb 100644
--- a/net/nimble/host/test/src/ble_hs_pvcy_test.c
+++ b/net/nimble/host/test/src/ble_hs_pvcy_test.c
@@ -24,6 +24,83 @@
 #include "host/ble_hs_test.h"
 #include "ble_hs_test_util.h"
 
+#define BLE_HS_PVCY_TEST_MAX_GAP_EVENTS 256
+static struct ble_gap_event
+ble_hs_pvcy_test_gap_events[BLE_HS_PVCY_TEST_MAX_GAP_EVENTS];
+static int ble_hs_pvcy_test_num_gap_events;
+
+static void
+ble_hs_pvcy_test_util_init(void)
+{
+    ble_hs_test_util_init();
+    ble_hs_pvcy_test_num_gap_events = 0;
+}
+
+static int
+ble_hs_pvcy_test_util_gap_event(struct ble_gap_event *event, void *arg) 
+{
+    TEST_ASSERT_FATAL(ble_hs_pvcy_test_num_gap_events <
+                      BLE_HS_PVCY_TEST_MAX_GAP_EVENTS);
+    ble_hs_pvcy_test_gap_events[ble_hs_pvcy_test_num_gap_events++] = *event;
+
+    return 0;
+}
+
+static void
+ble_hs_pvcy_test_util_all_gap_procs(int adv_status,
+                                    int conn_status,
+                                    int disc_status)
+{
+    struct ble_gap_disc_params disc_params;
+    ble_addr_t peer_addr;
+    int rc;
+
+    /* Advertise. */
+    rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
+                                    NULL, &ble_hs_test_util_adv_params,
+                                    BLE_HS_FOREVER,
+                                    ble_hs_pvcy_test_util_gap_event,
+                                    NULL, 0, 0);
+    TEST_ASSERT_FATAL(rc == adv_status);
+
+    if (rc == 0) {
+        rc = ble_hs_test_util_adv_stop(0);
+        TEST_ASSERT_FATAL(rc == 0);
+    }
+
+    /* Connect. */
+    peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
+    rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
+                                  BLE_HS_FOREVER, NULL,
+                                  ble_hs_pvcy_test_util_gap_event, NULL, 0);
+    TEST_ASSERT_FATAL(rc == conn_status);
+
+    if (rc == 0) {
+        ble_hs_test_util_conn_cancel_full();
+    }
+
+    /* Discover. */
+    disc_params = (struct ble_gap_disc_params){ 0 };
+    rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
+                               &disc_params, ble_hs_pvcy_test_util_gap_event,
+                               NULL, -1, 0);
+    TEST_ASSERT_FATAL(rc == disc_status);
+
+    if (rc == 0) {
+        rc = ble_hs_test_util_disc_cancel(0);
+        TEST_ASSERT_FATAL(rc == 0);
+    }
+}
+
+static void
+ble_hs_pvcy_test_util_add_irk_set_acks(void)
+{
+    ble_hs_test_util_hci_ack_append(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0); 
+    ble_hs_test_util_hci_ack_append(
+        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE), 0);
+}
+
 static void
 ble_hs_pvcy_test_util_start_host(int num_expected_irks)
 {
@@ -41,38 +118,75 @@ ble_hs_pvcy_test_util_start_host(int num_expected_irks)
     ble_hs_test_util_hci_ack_set_startup();
 
     for (i = 0; i < num_expected_irks; i++) {
-        ble_hs_test_util_hci_ack_append(
-            BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0); 
+        ble_hs_pvcy_test_util_add_irk_set_acks();
     }
 
     rc = ble_hs_start();
     TEST_ASSERT_FATAL(rc == 0);
 
     /* Discard startup HCI commands. */
-    ble_hs_test_util_hci_out_adj(11);
+    ble_hs_test_util_hci_out_adj(12);
+}
+
+static void
+ble_hs_pvcy_test_util_add_irk_verify_tx(const ble_addr_t *peer_addr,
+                                        const uint8_t *peer_irk,
+                                        const uint8_t *local_irk)
+{
+    ble_hs_test_util_hci_verify_tx_add_irk(peer_addr->type,
+                                           peer_addr->val,
+                                           peer_irk,
+                                           local_irk);
+
+    ble_hs_test_util_hci_verify_tx_set_priv_mode(peer_addr->type,
+                                                 peer_addr->val,
+                                                 BLE_GAP_PRIVATE_MODE_DEVICE);
 }
 
 static void
-ble_hs_pvcy_test_util_add_irk(const struct ble_store_value_sec *value_sec)
+ble_hs_pvcy_test_util_add_irk(const ble_addr_t *peer_addr,
+                              const uint8_t *peer_irk,
+                              const uint8_t *local_irk)
 {
     int rc;
 
-    ble_hs_test_util_hci_ack_set(
-        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0);
-    ble_hs_test_util_hci_ack_append(
-        BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE), 0);
+    ble_hs_pvcy_test_util_add_irk_set_acks();
 
-    rc = ble_store_write_peer_sec(value_sec);
+    rc = ble_hs_pvcy_add_entry(peer_addr->val, peer_addr->type, peer_irk);
     TEST_ASSERT_FATAL(rc == 0);
 
-    ble_hs_test_util_hci_verify_tx_add_irk(value_sec->peer_addr.type,
-                                           value_sec->peer_addr.val,
-                                           value_sec->irk,
-                                           ble_hs_pvcy_default_irk);
+    ble_hs_test_util_hci_out_adj(-2);
+    ble_hs_pvcy_test_util_add_irk_verify_tx(peer_addr, peer_irk, local_irk);
+}
 
-    ble_hs_test_util_hci_verify_tx_set_priv_mode(value_sec->peer_addr.type,
-                                                 value_sec->peer_addr.val,
-                                                 BLE_GAP_PRIVATE_MODE_DEVICE);
+static void
+ble_hs_pvcy_test_util_add_arbitrary_irk(void)
+{
+    ble_addr_t peer_addr;
+
+    peer_addr = (ble_addr_t) {
+        .type = BLE_ADDR_PUBLIC,
+        .val = {1,2,3,4,5,6},
+    };
+    ble_hs_pvcy_test_util_add_irk(
+        &peer_addr,
+        (uint8_t[16]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},
+        ble_hs_pvcy_default_irk);
+}
+
+static void
+ble_hs_pvcy_test_util_restore_irk(const struct ble_store_value_sec *value_sec)
+{
+    int rc;
+
+    ble_hs_pvcy_test_util_add_irk_set_acks();
+
+    rc = ble_store_write_peer_sec(value_sec);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec->peer_addr,
+                                            value_sec->irk,
+                                            ble_hs_pvcy_default_irk);
 }
 
 TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
@@ -80,7 +194,7 @@ TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
     struct ble_store_value_sec value_sec1;
     struct ble_store_value_sec value_sec2;
 
-    ble_hs_test_util_init();
+    ble_hs_pvcy_test_util_init();
 
     /*** No persisted IRKs. */
     ble_hs_pvcy_test_util_start_host(0);
@@ -96,14 +210,13 @@ TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
         .irk = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
         .irk_present = 1,
     };
-    ble_hs_pvcy_test_util_add_irk(&value_sec1);
+    ble_hs_pvcy_test_util_restore_irk(&value_sec1);
 
     /* Ensure it gets added to list on startup. */
     ble_hs_pvcy_test_util_start_host(1);
-    ble_hs_test_util_hci_verify_tx_add_irk(value_sec1.peer_addr.type,
-                                           value_sec1.peer_addr.val,
-                                           value_sec1.irk,
-                                           ble_hs_pvcy_default_irk);
+    ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
+                                            value_sec1.irk,
+                                            ble_hs_pvcy_default_irk);
 
     /* Two persisted IRKs. */
     value_sec2 = (struct ble_store_value_sec) {
@@ -114,18 +227,227 @@ TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
         .irk = { 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9, 9, 9, 10 },
         .irk_present = 1,
     };
-    ble_hs_pvcy_test_util_add_irk(&value_sec2);
+    ble_hs_pvcy_test_util_restore_irk(&value_sec2);
 
     /* Ensure both get added to list on startup. */
     ble_hs_pvcy_test_util_start_host(2);
-    ble_hs_test_util_hci_verify_tx_add_irk(value_sec1.peer_addr.type,
-                                           value_sec1.peer_addr.val,
-                                           value_sec1.irk,
-                                           ble_hs_pvcy_default_irk);
-    ble_hs_test_util_hci_verify_tx_add_irk(value_sec2.peer_addr.type,
-                                           value_sec2.peer_addr.val,
-                                           value_sec2.irk,
-                                           ble_hs_pvcy_default_irk);
+    ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
+                                            value_sec1.irk,
+                                            ble_hs_pvcy_default_irk);
+    ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec2.peer_addr,
+                                            value_sec2.irk,
+                                            ble_hs_pvcy_default_irk);
+}
+
+/** No active GAP procedures. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_idle)
+{
+    ble_hs_pvcy_test_util_init();
+
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
+}
+
+/*** Advertising active. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv)
+{
+    int rc;
+
+    ble_hs_pvcy_test_util_init();
+
+    /* Start an advertising procedure. */
+    rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
+                                    NULL, &ble_hs_test_util_adv_params,
+                                    BLE_HS_FOREVER,
+                                    ble_hs_pvcy_test_util_gap_event,
+                                    NULL, 0, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_test_util_hci_ack_set(
+        BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
+        0);
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
+                BLE_GAP_EVENT_ADV_COMPLETE);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure GAP procedures are no longer preempted. */
+    ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
+}
+
+/*** Discovery active. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_disc)
+{
+    struct ble_gap_disc_params disc_params;
+    int rc;
+
+    ble_hs_pvcy_test_util_init();
+
+    /* Start an advertising procedure. */
+    disc_params = (struct ble_gap_disc_params){ 0 };
+    rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
+                               &disc_params, ble_hs_pvcy_test_util_gap_event,
+                               NULL, -1, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_test_util_hci_ack_set(
+        BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
+        0);
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
+                BLE_GAP_EVENT_DISC_COMPLETE);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].disc_complete.reason ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure GAP procedures are no longer preempted. */
+    ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
+}
+
+/*** Connect active. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_conn)
+{
+    ble_addr_t peer_addr;
+    int rc;
+
+    ble_hs_pvcy_test_util_init();
+
+    /* Start a connect procedure. */
+    peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
+    rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
+                                  BLE_HS_FOREVER, NULL,
+                                  ble_hs_pvcy_test_util_gap_event, NULL, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_test_util_hci_ack_set(
+        BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL),
+        0);
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+
+    /* Cancel is now in progress. */
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
+
+    /* Ensure no GAP procedures are allowed. */
+    ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
+                                        BLE_HS_EALREADY,
+                                        BLE_HS_EBUSY);
+
+    /* Receive cancel event. */
+    ble_hs_test_util_rx_conn_cancel_evt();
+
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
+                BLE_GAP_EVENT_CONNECT);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].connect.status ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure GAP procedures are no longer preempted. */
+    ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
+}
+
+/*** Advertising and discovery active. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_disc)
+{
+    struct ble_gap_disc_params disc_params;
+    int rc;
+
+    ble_hs_pvcy_test_util_init();
+
+    /* Start an advertising procedure. */
+    rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
+                                    NULL, &ble_hs_test_util_adv_params,
+                                    BLE_HS_FOREVER,
+                                    ble_hs_pvcy_test_util_gap_event,
+                                    NULL, 0, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    /* Start a discovery procedure. */
+    disc_params = (struct ble_gap_disc_params){ 0 };
+    rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
+                               &disc_params, ble_hs_pvcy_test_util_gap_event,
+                               NULL, -1, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) {
+        { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 },
+        { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE), 0 },
+        { 0 },
+    });
+
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
+                BLE_GAP_EVENT_ADV_COMPLETE);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
+                BLE_HS_EPREEMPTED);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
+                BLE_GAP_EVENT_DISC_COMPLETE);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].disc_complete.reason ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure GAP procedures are no longer preempted. */
+    ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
+}
+
+/*** Advertising and connecting active. */
+TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_conn)
+{
+    ble_addr_t peer_addr;
+    int rc;
+
+    ble_hs_pvcy_test_util_init();
+
+    /* Start an advertising procedure. */
+    rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
+                                    NULL, &ble_hs_test_util_adv_params,
+                                    BLE_HS_FOREVER,
+                                    ble_hs_pvcy_test_util_gap_event,
+                                    NULL, 0, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    /* Start a connect procedure. */
+    peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
+    rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
+                                  BLE_HS_FOREVER, NULL,
+                                  ble_hs_pvcy_test_util_gap_event, NULL, 0);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) {
+        { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 },
+        { BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL), 0 },
+        { 0 },
+    });
+
+    ble_hs_pvcy_test_util_add_arbitrary_irk();
+
+    /* Cancel is now in progress. */
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
+                BLE_GAP_EVENT_ADV_COMPLETE);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure no GAP procedures are allowed. */
+    ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
+                                        BLE_HS_EALREADY,
+                                        BLE_HS_EBUSY);
+
+    /* Receive cancel event. */
+    ble_hs_test_util_rx_conn_cancel_evt();
+
+    TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
+                BLE_GAP_EVENT_CONNECT);
+    TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].connect.status ==
+                BLE_HS_EPREEMPTED);
+
+    /* Ensure GAP procedures are no longer preempted. */
+    ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
 }
 
 TEST_SUITE(ble_hs_pvcy_test_suite_irk)
@@ -133,6 +455,12 @@ TEST_SUITE(ble_hs_pvcy_test_suite_irk)
     tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
 
     ble_hs_pvcy_test_case_restore_irks();
+    ble_hs_pvcy_test_case_add_irk_idle();
+    ble_hs_pvcy_test_case_add_irk_adv();
+    ble_hs_pvcy_test_case_add_irk_disc();
+    ble_hs_pvcy_test_case_add_irk_conn();
+    ble_hs_pvcy_test_case_add_irk_adv_disc();
+    ble_hs_pvcy_test_case_add_irk_adv_conn();
 }
 
 int
diff --git a/net/nimble/host/test/src/ble_hs_test_util.c 
b/net/nimble/host/test/src/ble_hs_test_util.c
index 88646be2e..b6972d160 100644
--- a/net/nimble/host/test/src/ble_hs_test_util.c
+++ b/net/nimble/host/test/src/ble_hs_test_util.c
@@ -304,8 +304,11 @@ ble_hs_test_util_connect(uint8_t own_addr_type, const 
ble_addr_t *peer_addr,
 
     rc = ble_gap_connect(own_addr_type, peer_addr, duration_ms, params, cb,
                          cb_arg);
-
-    TEST_ASSERT(rc == BLE_HS_HCI_ERR(ack_status));
+    if (ack_status != 0) {
+        TEST_ASSERT(rc == BLE_HS_HCI_ERR(ack_status));
+    } else if (rc != 0) {
+        return rc;
+    }
 
     if (params == NULL) {
         ble_hs_test_util_conn_params_dflt(&dflt_params);
@@ -334,12 +337,19 @@ ble_hs_test_util_conn_cancel(uint8_t ack_status)
 }
 
 void
-ble_hs_test_util_conn_cancel_full(void)
+ble_hs_test_util_rx_conn_cancel_evt(void)
 {
     ble_hs_test_util_conn_cancel(0);
     ble_hs_test_util_hci_rx_conn_cancel_evt();
 }
 
+void
+ble_hs_test_util_conn_cancel_full(void)
+{
+    ble_hs_test_util_conn_cancel(0);
+    ble_hs_test_util_rx_conn_cancel_evt();
+}
+
 int
 ble_hs_test_util_conn_terminate(uint16_t conn_handle, uint8_t hci_status)
 {
@@ -617,6 +627,10 @@ ble_hs_test_util_set_our_irk(const uint8_t *irk, int 
fail_idx,
             ble_hs_test_util_hci_misc_exp_status(4, fail_idx, hci_status),
         },
         {
+            BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_PRIVACY_MODE),
+            ble_hs_test_util_hci_misc_exp_status(4, fail_idx, hci_status),
+        },
+        {
             0
         }
     }));
diff --git a/net/nimble/host/test/src/ble_hs_test_util.h 
b/net/nimble/host/test/src/ble_hs_test_util.h
index 725fe5d64..2640adf69 100644
--- a/net/nimble/host/test/src/ble_hs_test_util.h
+++ b/net/nimble/host/test/src/ble_hs_test_util.h
@@ -100,6 +100,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_rx_conn_cancel_evt(void);
 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_rx_disconn_complete(uint16_t conn_handle,
diff --git a/net/nimble/host/test/src/ble_hs_test_util_hci.c 
b/net/nimble/host/test/src/ble_hs_test_util_hci.c
index af03da85c..32548c666 100644
--- a/net/nimble/host/test/src/ble_hs_test_util_hci.c
+++ b/net/nimble/host/test/src/ble_hs_test_util_hci.c
@@ -189,7 +189,7 @@ ble_hs_test_util_hci_ack_set(uint16_t opcode, uint8_t 
status)
 
 void
 ble_hs_test_util_hci_ack_append_params(uint16_t opcode, uint8_t status,
-                                   void *params, uint8_t params_len)
+                                       void *params, uint8_t params_len)
 {
     struct ble_hs_test_util_hci_ack *ack;
 
@@ -280,13 +280,17 @@ ble_hs_test_util_hci_ack_set_startup(void)
             .opcode = ble_hs_hci_util_opcode_join(
                 BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST),
         },
+        {
+            .opcode = ble_hs_hci_util_opcode_join(
+                BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE),
+        },
         { 0 }
     }));
 }
 
 void
 ble_hs_test_util_hci_ack_set_disc(uint8_t own_addr_type,
-                               int fail_idx, uint8_t fail_status)
+                                  int fail_idx, uint8_t fail_status)
 {
     static bool privacy_enabled;
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to