]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/scan.c
Fix SELECT_NETWORK freq parameter
[thirdparty/hostap.git] / wpa_supplicant / scan.c
index 3c3f9e01d0c646db8119f1106ac0de1244190460..f3ff4cdd828b65d675547e4feef45801d02c532a 100644 (file)
@@ -36,8 +36,7 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
 
        if (wpa_s->current_ssid == NULL) {
                wpa_s->current_ssid = ssid;
-               if (wpa_s->current_ssid != NULL)
-                       wpas_notify_network_changed(wpa_s);
+               wpas_notify_network_changed(wpa_s);
        }
        wpa_supplicant_initiate_eapol(wpa_s);
        wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
@@ -60,10 +59,7 @@ static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
 
                wps = 1;
                *req_type = wpas_wps_get_req_type(ssid);
-               if (!ssid->eap.phase1)
-                       continue;
-
-               if (os_strstr(ssid->eap.phase1, "pbc=1"))
+               if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1"))
                        return 2;
        }
 
@@ -166,6 +162,8 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
        if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
                wpa_msg(wpa_s, MSG_INFO,
                        "Failed to assign random MAC address for a scan");
+               wpa_scan_free_params(params);
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
                radio_work_done(work);
                return;
        }
@@ -178,6 +176,17 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
                params->only_new_results = 1;
        }
        ret = wpa_drv_scan(wpa_s, params);
+       /*
+        * Store the obtained vendor scan cookie (if any) in wpa_s context.
+        * The current design is to allow only one scan request on each
+        * interface, hence having this scan cookie stored in wpa_s context is
+        * fine for now.
+        *
+        * Revisit this logic if concurrent scan operations per interface
+        * is supported.
+        */
+       if (ret == 0)
+               wpa_s->curr_scan_cookie = params->scan_cookie;
        wpa_scan_free_params(params);
        work->ctx = NULL;
        if (ret) {
@@ -229,12 +238,11 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
        }
 
        ctx = wpa_scan_clone_params(params);
-       if (ctx == NULL)
-               return -1;
-
-       if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+       if (!ctx ||
+           radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
        {
                wpa_scan_free_params(ctx);
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
                return -1;
        }
 
@@ -266,8 +274,9 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-                                   struct wpa_driver_scan_params *params)
+static int
+wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+                               struct wpa_driver_scan_params *params)
 {
        int ret;
 
@@ -282,7 +291,7 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
 }
 
 
-int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
 {
        int ret;
 
@@ -428,6 +437,39 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_INTERWORKING */
 
 
+void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
+{
+       struct wpabuf *default_ies = NULL;
+       u8 ext_capab[18];
+       int ext_capab_len;
+       enum wpa_driver_if_type type = WPA_IF_STATION;
+
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
+               type = WPA_IF_P2P_CLIENT;
+#endif /* CONFIG_P2P */
+
+       wpa_drv_get_ext_capa(wpa_s, type);
+
+       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                            sizeof(ext_capab));
+       if (ext_capab_len > 0 &&
+           wpabuf_resize(&default_ies, ext_capab_len) == 0)
+               wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
+
+#ifdef CONFIG_MBO
+       /* Send cellular capabilities for potential MBO STAs */
+       if (wpabuf_resize(&default_ies, 9) == 0)
+               wpas_mbo_scan_ie(wpa_s, default_ies);
+#endif /* CONFIG_MBO */
+
+       if (default_ies)
+               wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies),
+                                            wpabuf_len(default_ies));
+       wpabuf_free(default_ies);
+}
+
+
 static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
 {
        struct wpabuf *extra_ie = NULL;
@@ -438,6 +480,13 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
        enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+       else
+#endif /* CONFIG_P2P */
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
        ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
                                             sizeof(ext_capab));
        if (ext_capab_len > 0 &&
@@ -490,6 +539,19 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
                wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_MBO
+       /* Send cellular capabilities for potential MBO STAs */
+       if (wpabuf_resize(&extra_ie, 9) == 0)
+               wpas_mbo_scan_ie(wpa_s, extra_ie);
+#endif /* CONFIG_MBO */
+
+       if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
+               struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
+
+               if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
+                       wpabuf_put_buf(extra_ie, buf);
+       }
+
        return extra_ie;
 }
 
@@ -521,21 +583,6 @@ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_P2P */
 
 
-static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
-                                         u16 num_modes,
-                                         enum hostapd_hw_mode mode)
-{
-       u16 i;
-
-       for (i = 0; i < num_modes; i++) {
-               if (modes[i].mode == mode)
-                       return &modes[i];
-       }
-
-       return NULL;
-}
-
-
 static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
                                        enum hostapd_hw_mode band,
                                        struct wpa_driver_scan_params *params)
@@ -585,6 +632,12 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
        unsigned int i;
        struct wpa_ssid *ssid;
 
+       /*
+        * For devices with max_ssids greater than 1, leave the last slot empty
+        * for adding the wildcard scan entry.
+        */
+       max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
+
        for (i = 0; i < wpa_s->scan_id_count; i++) {
                unsigned int j;
 
@@ -661,10 +714,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
        size_t max_ssids;
        int connect_without_scan = 0;
 
-       if (wpa_s->pno || wpa_s->pno_sched_pending) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
-               return;
-       }
+       wpa_s->ignore_post_flush_scan_res = 0;
 
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
@@ -726,6 +776,21 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                return;
        }
 
+       /*
+        * Don't cancel the scan based on ongoing PNO; defer it. Some scans are
+        * used for changing modes inside wpa_supplicant (roaming,
+        * auto-reconnect, etc). Discarding the scan might hurt these processes.
+        * The normal use case for PNO is to suspend the host immediately after
+        * starting PNO, so the periodic 100 ms attempts to run the scan do not
+        * normally happen in practice multiple times, i.e., this is simply
+        * restarting scanning once the host is woken up and PNO stopped.
+        */
+       if (wpa_s->pno || wpa_s->pno_sched_pending) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
+               wpa_supplicant_req_scan(wpa_s, 0, 100000);
+               return;
+       }
+
        if (wpa_s->conf->ap_scan == 2)
                max_ssids = 1;
        else {
@@ -839,12 +904,10 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                 * slot for the zero-terminator.
                 */
                params.freqs = os_malloc(sizeof(int) * 2);
-               if (params.freqs == NULL) {
-                       wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
-                       return;
+               if (params.freqs) {
+                       params.freqs[0] = wpa_s->assoc_freq;
+                       params.freqs[1] = 0;
                }
-               params.freqs[0] = wpa_s->assoc_freq;
-               params.freqs[1] = 0;
 
                /*
                 * Reset the reattach flag so that we fall back to full scan if
@@ -955,6 +1018,13 @@ ssid_list_set:
                wpa_s->manual_scan_freqs = NULL;
        }
 
+       if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Limit select_network scan to specified channels");
+               params.freqs = wpa_s->select_network_scan_freqs;
+               wpa_s->select_network_scan_freqs = NULL;
+       }
+
        if (params.freqs == NULL && wpa_s->next_scan_freqs) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
                        "generated frequency list");
@@ -1007,7 +1077,8 @@ ssid_list_set:
        }
 #endif /* CONFIG_P2P */
 
-       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+       if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
+           wpa_s->wpa_state <= WPA_SCANNING) {
                params.mac_addr_rand = 1;
                if (wpa_s->mac_addr_scan) {
                        params.mac_addr = wpa_s->mac_addr_scan;
@@ -1015,6 +1086,27 @@ ssid_list_set:
                }
        }
 
+       if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
+               struct wpa_bss *bss;
+
+               params.bssid = wpa_s->next_scan_bssid;
+               bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
+               if (bss && bss->ssid_len && params.num_ssids == 1 &&
+                   params.ssids[0].ssid_len == 0) {
+                       params.ssids[0].ssid = bss->ssid;
+                       params.ssids[0].ssid_len = bss->ssid_len;
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Scan a previously specified BSSID " MACSTR
+                               " and SSID %s",
+                               MAC2STR(params.bssid),
+                               wpa_ssid_txt(bss->ssid, bss->ssid_len));
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Scan a previously specified BSSID " MACSTR,
+                               MAC2STR(params.bssid));
+               }
+       }
+
        scan_params = &params;
 
 scan:
@@ -1075,6 +1167,8 @@ scan:
 #ifdef CONFIG_INTERWORKING
                wpa_s->interworking_fast_assoc_tried = 0;
 #endif /* CONFIG_INTERWORKING */
+               if (params.bssid)
+                       os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
        }
 }
 
@@ -1162,6 +1256,26 @@ int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
 }
 
 
+static void
+wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
+                                 struct wpa_driver_scan_params *params)
+{
+       if (wpa_s->wpa_state != WPA_COMPLETED ||
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
+           wpa_s->srp.relative_rssi_set == 0)
+               return;
+
+       params->relative_rssi_set = 1;
+       params->relative_rssi = wpa_s->srp.relative_rssi;
+
+       if (wpa_s->srp.relative_adjust_rssi == 0)
+               return;
+
+       params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
+       params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
+}
+
+
 /**
  * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1193,6 +1307,8 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
                return -1;
 
+       wpa_s->sched_scan_stop_req = 0;
+
        if (wpa_s->sched_scanning) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
                return 0;
@@ -1404,7 +1520,8 @@ scan:
 
        wpa_setband_scan_freqs(wpa_s, scan_params);
 
-       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+       if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
+           wpa_s->wpa_state <= WPA_SCANNING) {
                params.mac_addr_rand = 1;
                if (wpa_s->mac_addr_sched_scan) {
                        params.mac_addr = wpa_s->mac_addr_sched_scan;
@@ -1413,6 +1530,8 @@ scan:
                }
        }
 
+       wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
+
        ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
        wpabuf_free(extra_ie);
        os_free(params.filter_ssids);
@@ -1491,6 +1610,9 @@ void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
        if (!wpa_s->sched_scanning)
                return;
 
+       if (wpa_s->sched_scanning)
+               wpa_s->sched_scan_stop_req = 1;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
        eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
        wpa_supplicant_stop_sched_scan(wpa_s);
@@ -1550,20 +1672,13 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
  */
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 {
-       const u8 *end, *pos;
-
-       pos = (const u8 *) (res + 1);
-       end = pos + res->ie_len;
+       size_t ie_len = res->ie_len;
 
-       while (end - pos > 1) {
-               if (2 + pos[1] > end - pos)
-                       break;
-               if (pos[0] == ie)
-                       return pos;
-               pos += 2 + pos[1];
-       }
+       /* Use the Beacon frame IEs if res->ie_len is not available */
+       if (!ie_len)
+               ie_len = res->beacon_ie_len;
 
-       return NULL;
+       return get_ie((const u8 *) (res + 1), ie_len, ie);
 }
 
 
@@ -1680,10 +1795,12 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
  * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
  * rule of thumb is that any SNR above 20 is good." This one
  * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
- * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
- * conservative value.
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. The estimates used in
+ * scan_est_throughput() allow even smaller SNR values for the maximum rates
+ * (21 for 54 Mbps, 22 for VHT80 MCS9, 24 for HT40 and HT20 MCS7). Use 25 as a
+ * somewhat conservative value here.
  */
-#define GREAT_SNR 30
+#define GREAT_SNR 25
 
 #define IS_5GHZ(n) (n > 4000)
 
@@ -1731,10 +1848,12 @@ static int wpa_scan_result_compar(const void *a, const void *b)
        }
 
        /* if SNR is close, decide by max rate or frequency band */
-       if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
-           (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+       if (snr_a && snr_b && abs(snr_b - snr_a) < 7) {
                if (wa->est_throughput != wb->est_throughput)
                        return wb->est_throughput - wa->est_throughput;
+       }
+       if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+           (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
                if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
                        return IS_5GHZ(wa->freq) ? -1 : 1;
        }
@@ -1866,8 +1985,8 @@ int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
 }
 
 
-static void filter_scan_res(struct wpa_supplicant *wpa_s,
-                           struct wpa_scan_results *res)
+void filter_scan_res(struct wpa_supplicant *wpa_s,
+                    struct wpa_scan_results *res)
 {
        size_t i, j;
 
@@ -1894,13 +2013,13 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s,
 
 /*
  * Noise floor values to use when we have signal strength
- * measurements, but no noise floor measurments. These values were
+ * measurements, but no noise floor measurements. These values were
  * measured in an office environment with many APs.
  */
 #define DEFAULT_NOISE_FLOOR_2GHZ (-89)
 #define DEFAULT_NOISE_FLOOR_5GHZ (-92)
 
-static void scan_snr(struct wpa_scan_res *res)
+void scan_snr(struct wpa_scan_res *res)
 {
        if (res->flags & WPA_SCAN_NOISE_INVALID) {
                res->noise = IS_5GHZ(res->freq) ?
@@ -1984,8 +2103,8 @@ static unsigned int max_vht80_rate(int snr)
 }
 
 
-static void scan_est_throughput(struct wpa_supplicant *wpa_s,
-                               struct wpa_scan_res *res)
+void scan_est_throughput(struct wpa_supplicant *wpa_s,
+                        struct wpa_scan_res *res)
 {
        enum local_hw_capab capab = wpa_s->hw_capab;
        int rate; /* max legacy rate in 500 kb/s units */
@@ -2122,10 +2241,22 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_WPS */
 
-       qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
-             compar);
+       if (scan_res->res) {
+               qsort(scan_res->res, scan_res->num,
+                     sizeof(struct wpa_scan_res *), compar);
+       }
        dump_scan_res(scan_res);
 
+       if (wpa_s->ignore_post_flush_scan_res) {
+               /* FLUSH command aborted an ongoing scan and these are the
+                * results from the aborted scan. Do not process the results to
+                * maintain flushed state. */
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Do not update BSS table based on pending post-FLUSH scan results");
+               wpa_s->ignore_post_flush_scan_res = 0;
+               return scan_res;
+       }
+
        wpa_bss_update_start(wpa_s);
        for (i = 0; i < scan_res->num; i++)
                wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
@@ -2250,6 +2381,8 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
        params->p2p_probe = src->p2p_probe;
        params->only_new_results = src->only_new_results;
        params->low_priority = src->low_priority;
+       params->duration = src->duration;
+       params->duration_mandatory = src->duration_mandatory;
 
        if (src->sched_scan_plans_num > 0) {
                params->sched_scan_plans =
@@ -2281,6 +2414,21 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
                        params->mac_addr_mask = mac_addr + ETH_ALEN;
                }
        }
+
+       if (src->bssid) {
+               u8 *bssid;
+
+               bssid = os_malloc(ETH_ALEN);
+               if (!bssid)
+                       goto failed;
+               os_memcpy(bssid, src->bssid, ETH_ALEN);
+               params->bssid = bssid;
+       }
+
+       params->relative_rssi_set = src->relative_rssi_set;
+       params->relative_rssi = src->relative_rssi;
+       params->relative_adjust_band = src->relative_adjust_band;
+       params->relative_adjust_rssi = src->relative_adjust_rssi;
        return params;
 
 failed:
@@ -2309,6 +2457,8 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params)
         */
        os_free((u8 *) params->mac_addr);
 
+       os_free((u8 *) params->bssid);
+
        os_free(params);
 }
 
@@ -2320,15 +2470,23 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
        struct wpa_ssid *ssid;
        struct wpa_driver_scan_params params;
        struct sched_scan_plan scan_plan;
+       unsigned int max_sched_scan_ssids;
 
        if (!wpa_s->sched_scan_supported)
                return -1;
 
+       if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+               max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+       else
+               max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+       if (max_sched_scan_ssids < 1)
+               return -1;
+
        if (wpa_s->pno || wpa_s->pno_sched_pending)
                return 0;
 
        if ((wpa_s->wpa_state > WPA_SCANNING) &&
-           (wpa_s->wpa_state <= WPA_COMPLETED)) {
+           (wpa_s->wpa_state < WPA_COMPLETED)) {
                wpa_printf(MSG_ERROR, "PNO: In assoc process");
                return -EAGAIN;
        }
@@ -2344,6 +2502,13 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                }
        }
 
+       if (wpa_s->sched_scan_stop_req) {
+               wpa_printf(MSG_DEBUG,
+                          "Schedule PNO after previous sched scan has stopped");
+               wpa_s->pno_sched_pending = 1;
+               return 0;
+       }
+
        os_memset(&params, 0, sizeof(params));
 
        num_ssid = num_match_ssid = 0;
@@ -2367,10 +2532,10 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                num_ssid++;
        }
 
-       if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+       if (num_ssid > max_sched_scan_ssids) {
                wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
-                          "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
-               num_ssid = WPAS_MAX_SCAN_SSIDS;
+                          "%u", max_sched_scan_ssids, (unsigned int) num_ssid);
+               num_ssid = max_sched_scan_ssids;
        }
 
        if (num_match_ssid > wpa_s->max_match_sets) {
@@ -2433,7 +2598,8 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                params.freqs = wpa_s->manual_sched_scan_freqs;
        }
 
-       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+       if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
+           wpa_s->wpa_state <= WPA_SCANNING) {
                params.mac_addr_rand = 1;
                if (wpa_s->mac_addr_pno) {
                        params.mac_addr = wpa_s->mac_addr_pno;
@@ -2441,6 +2607,8 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                }
        }
 
+       wpa_scan_set_relative_rssi_params(wpa_s, &params);
+
        ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
        os_free(params.filter_ssids);
        if (ret == 0)
@@ -2459,6 +2627,7 @@ int wpas_stop_pno(struct wpa_supplicant *wpa_s)
                return 0;
 
        ret = wpa_supplicant_stop_sched_scan(wpa_s);
+       wpa_s->sched_scan_stop_req = 1;
 
        wpa_s->pno = 0;
        wpa_s->pno_sched_pending = 0;
@@ -2530,12 +2699,20 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
 
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
 {
-       if (wpa_s->scan_work && wpa_s->own_scan_running) {
+       struct wpa_radio_work *work;
+       struct wpa_radio *radio = wpa_s->radio;
+
+       dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+               if (work->wpa_s != wpa_s || !work->started ||
+                   (os_strcmp(work->type, "scan") != 0 &&
+                    os_strcmp(work->type, "p2p-scan") != 0))
+                       continue;
                wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
-               return wpa_drv_abort_scan(wpa_s);
+               return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
        }
 
-       return 0;
+       wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
+       return -1;
 }
 
 
@@ -2576,13 +2753,6 @@ int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
                        goto fail;
                }
 
-               if (!scan_plan->interval) {
-                       wpa_printf(MSG_ERROR,
-                                  "scan plan %u: Interval cannot be zero",
-                                  num);
-                       goto fail;
-               }
-
                if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
                        wpa_printf(MSG_WARNING,
                                   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
@@ -2656,3 +2826,31 @@ fail:
        wpa_printf(MSG_ERROR, "invalid scan plans list");
        return -1;
 }
+
+
+/**
+ * wpas_scan_reset_sched_scan - Reset sched_scan state
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel a running scheduled scan and to reset an
+ * internal scan state to continue with a regular scan on the following
+ * wpa_supplicant_req_scan() calls.
+ */
+void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->normal_scans = 0;
+       if (wpa_s->sched_scanning) {
+               wpa_s->sched_scan_timed_out = 0;
+               wpa_s->prev_sched_ssid = NULL;
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+       }
+}
+
+
+void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s)
+{
+       /* simulate timeout to restart the sched scan */
+       wpa_s->sched_scan_timed_out = 1;
+       wpa_s->prev_sched_ssid = NULL;
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+}