Updated to hostap_2_6
[mech_eap.git] / libeap / wpa_supplicant / scan.c
index d7049a1..fb8ebdf 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;
        }
@@ -229,12 +227,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,14 +263,14 @@ 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,
-                                   int interval)
+static int
+wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+                               struct wpa_driver_scan_params *params)
 {
        int ret;
 
        wpa_supplicant_notify_scanning(wpa_s, 1);
-       ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+       ret = wpa_drv_sched_scan(wpa_s, params);
        if (ret)
                wpa_supplicant_notify_scanning(wpa_s, 0);
        else
@@ -283,7 +280,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;
 
@@ -429,6 +426,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;
@@ -439,6 +469,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 &&
@@ -491,6 +528,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;
 }
 
@@ -522,21 +572,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)
@@ -586,6 +621,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;
 
@@ -840,12 +881,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
@@ -1016,6 +1055,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:
@@ -1076,6 +1136,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);
        }
 }
 
@@ -1182,6 +1244,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        unsigned int max_sched_scan_ssids;
        int wildcard = 0;
        int need_ssids;
+       struct sched_scan_plan scan_plan;
 
        if (!wpa_s->sched_scan_supported)
                return -1;
@@ -1193,6 +1256,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;
@@ -1271,11 +1336,6 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 
        if (!ssid || !wpa_s->prev_sched_ssid) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
-               if (wpa_s->conf->sched_scan_interval)
-                       wpa_s->sched_scan_interval =
-                               wpa_s->conf->sched_scan_interval;
-               if (wpa_s->sched_scan_interval == 0)
-                       wpa_s->sched_scan_interval = 10;
                wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
                wpa_s->first_sched_scan = 1;
                ssid = wpa_s->conf->ssid;
@@ -1360,14 +1420,51 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        scan_params = &params;
 
 scan:
-       if (ssid || !wpa_s->first_sched_scan) {
+       wpa_s->sched_scan_timed_out = 0;
+
+       /*
+        * We cannot support multiple scan plans if the scan request includes
+        * too many SSID's, so in this case use only the last scan plan and make
+        * it run infinitely. It will be stopped by the timeout.
+        */
+       if (wpa_s->sched_scan_plans_num == 1 ||
+           (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+               params.sched_scan_plans = wpa_s->sched_scan_plans;
+               params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+       } else if (wpa_s->sched_scan_plans_num > 1) {
                wpa_dbg(wpa_s, MSG_DEBUG,
-                       "Starting sched scan: interval %d timeout %d",
-                       wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+                       "Too many SSIDs. Default to using single scheduled_scan plan");
+               params.sched_scan_plans =
+                       &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
+                                                1];
+               params.sched_scan_plans_num = 1;
        } else {
+               if (wpa_s->conf->sched_scan_interval)
+                       scan_plan.interval = wpa_s->conf->sched_scan_interval;
+               else
+                       scan_plan.interval = 10;
+
+               if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+                       wpa_printf(MSG_WARNING,
+                                  "Scan interval too long(%u), use the maximum allowed(%u)",
+                                  scan_plan.interval,
+                                  wpa_s->max_sched_scan_plan_interval);
+                       scan_plan.interval =
+                               wpa_s->max_sched_scan_plan_interval;
+               }
+
+               scan_plan.iterations = 0;
+               params.sched_scan_plans = &scan_plan;
+               params.sched_scan_plans_num = 1;
+       }
+
+       if (ssid || !wpa_s->first_sched_scan) {
                wpa_dbg(wpa_s, MSG_DEBUG,
-                       "Starting sched scan: interval %d (no timeout)",
-                       wpa_s->sched_scan_interval);
+                       "Starting sched scan: interval %u timeout %d",
+                       params.sched_scan_plans[0].interval,
+                       wpa_s->sched_scan_timeout);
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
        }
 
        wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1381,8 +1478,7 @@ scan:
                }
        }
 
-       ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
-                                             wpa_s->sched_scan_interval);
+       ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
        wpabuf_free(extra_ie);
        os_free(params.filter_ssids);
        if (ret) {
@@ -1400,9 +1496,12 @@ scan:
                                       wpa_s, NULL);
                wpa_s->first_sched_scan = 0;
                wpa_s->sched_scan_timeout /= 2;
-               wpa_s->sched_scan_interval *= 2;
-               if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
-                       wpa_s->sched_scan_interval = 10;
+               params.sched_scan_plans[0].interval *= 2;
+               if ((unsigned int) wpa_s->sched_scan_timeout <
+                   params.sched_scan_plans[0].interval ||
+                   params.sched_scan_plans[0].interval >
+                   wpa_s->max_sched_scan_plan_interval) {
+                       params.sched_scan_plans[0].interval = 10;
                        wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
                }
        }
@@ -1457,6 +1556,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);
@@ -1516,20 +1618,7 @@ 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;
-
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
-                       break;
-               if (pos[0] == ie)
-                       return pos;
-               pos += 2 + pos[1];
-       }
-
-       return NULL;
+       return get_ie((const u8 *) (res + 1), res->ie_len, ie);
 }
 
 
@@ -1550,8 +1639,8 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
        pos = (const u8 *) (res + 1);
        end = pos + res->ie_len;
 
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
+       while (end - pos > 1) {
+               if (2 + pos[1] > end - pos)
                        break;
                if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
                    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1587,8 +1676,8 @@ const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
        pos += res->ie_len;
        end = pos + res->beacon_ie_len;
 
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
+       while (end - pos > 1) {
+               if (2 + pos[1] > end - pos)
                        break;
                if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
                    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1623,8 +1712,8 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
        pos = (const u8 *) (res + 1);
        end = pos + res->ie_len;
 
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
+       while (end - pos > 1) {
+               if (2 + pos[1] > end - pos)
                        break;
                if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
                    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1832,8 +1921,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;
 
@@ -1860,13 +1949,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) ?
@@ -1950,8 +2039,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 */
@@ -2148,6 +2237,9 @@ void scan_only_handler(struct wpa_supplicant *wpa_s,
                wpa_s->scan_work = NULL;
                radio_work_done(work);
        }
+
+       if (wpa_s->wpa_state == WPA_SCANNING)
+               wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 }
 
 
@@ -2214,6 +2306,19 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
        params->only_new_results = src->only_new_results;
        params->low_priority = src->low_priority;
 
+       if (src->sched_scan_plans_num > 0) {
+               params->sched_scan_plans =
+                       os_malloc(sizeof(*src->sched_scan_plans) *
+                                 src->sched_scan_plans_num);
+               if (!params->sched_scan_plans)
+                       goto failed;
+
+               os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
+                         sizeof(*src->sched_scan_plans) *
+                         src->sched_scan_plans_num);
+               params->sched_scan_plans_num = src->sched_scan_plans_num;
+       }
+
        if (src->mac_addr_rand) {
                params->mac_addr_rand = src->mac_addr_rand;
 
@@ -2231,6 +2336,17 @@ 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;
+       }
+
        return params;
 
 failed:
@@ -2251,6 +2367,7 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params)
        os_free((u8 *) params->extra_ies);
        os_free(params->freqs);
        os_free(params->filter_ssids);
+       os_free(params->sched_scan_plans);
 
        /*
         * Note: params->mac_addr_mask points to same memory allocation and
@@ -2258,20 +2375,31 @@ 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);
 }
 
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-       int ret, interval, prio;
+       int ret, prio;
        size_t i, num_ssid, num_match_ssid;
        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;
 
@@ -2292,6 +2420,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;
@@ -2315,10 +2450,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) {
@@ -2361,8 +2496,20 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->filter_rssi)
                params.filter_rssi = wpa_s->conf->filter_rssi;
 
-       interval = wpa_s->conf->sched_scan_interval ?
-               wpa_s->conf->sched_scan_interval : 10;
+       if (wpa_s->sched_scan_plans_num) {
+               params.sched_scan_plans = wpa_s->sched_scan_plans;
+               params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+       } else {
+               /* Set one scan plan that will run infinitely */
+               if (wpa_s->conf->sched_scan_interval)
+                       scan_plan.interval = wpa_s->conf->sched_scan_interval;
+               else
+                       scan_plan.interval = 10;
+
+               scan_plan.iterations = 0;
+               params.sched_scan_plans = &scan_plan;
+               params.sched_scan_plans_num = 1;
+       }
 
        if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2377,7 +2524,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s)
                }
        }
 
-       ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+       ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
        os_free(params.filter_ssids);
        if (ret == 0)
                wpa_s->pno = 1;
@@ -2395,6 +2542,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;
@@ -2462,3 +2610,160 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
        wpa_s->mac_addr_rand_enable |= type;
        return 0;
 }
+
+
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
+{
+       int scan_work = !!wpa_s->scan_work;
+
+#ifdef CONFIG_P2P
+       scan_work |= !!wpa_s->p2p_scan_work;
+#endif /* CONFIG_P2P */
+
+       if (scan_work && wpa_s->own_scan_running) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
+               return wpa_drv_abort_scan(wpa_s);
+       }
+
+       return 0;
+}
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+       struct sched_scan_plan *scan_plans = NULL;
+       const char *token, *context = NULL;
+       unsigned int num = 0;
+
+       if (!cmd)
+               return -1;
+
+       if (!cmd[0]) {
+               wpa_printf(MSG_DEBUG, "Clear sched scan plans");
+               os_free(wpa_s->sched_scan_plans);
+               wpa_s->sched_scan_plans = NULL;
+               wpa_s->sched_scan_plans_num = 0;
+               return 0;
+       }
+
+       while ((token = cstr_token(cmd, " ", &context))) {
+               int ret;
+               struct sched_scan_plan *scan_plan, *n;
+
+               n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+               if (!n)
+                       goto fail;
+
+               scan_plans = n;
+               scan_plan = &scan_plans[num];
+               num++;
+
+               ret = sscanf(token, "%u:%u", &scan_plan->interval,
+                            &scan_plan->iterations);
+               if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+                       wpa_printf(MSG_ERROR,
+                                  "Invalid sched scan plan input: %s", token);
+                       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)",
+                                  num, scan_plan->interval,
+                                  wpa_s->max_sched_scan_plan_interval);
+                       scan_plan->interval =
+                               wpa_s->max_sched_scan_plan_interval;
+               }
+
+               if (ret == 1) {
+                       scan_plan->iterations = 0;
+                       break;
+               }
+
+               if (!scan_plan->iterations) {
+                       wpa_printf(MSG_ERROR,
+                                  "scan plan %u: Number of iterations cannot be zero",
+                                  num);
+                       goto fail;
+               }
+
+               if (scan_plan->iterations >
+                   wpa_s->max_sched_scan_plan_iterations) {
+                       wpa_printf(MSG_WARNING,
+                                  "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
+                                  num, scan_plan->iterations,
+                                  wpa_s->max_sched_scan_plan_iterations);
+                       scan_plan->iterations =
+                               wpa_s->max_sched_scan_plan_iterations;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "scan plan %u: interval=%u iterations=%u",
+                          num, scan_plan->interval, scan_plan->iterations);
+       }
+
+       if (!scan_plans) {
+               wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+               goto fail;
+       }
+
+       if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+               wpa_printf(MSG_ERROR,
+                          "All scan plans but the last must specify a number of iterations");
+               goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
+                  num, scan_plans[num - 1].interval);
+
+       if (num > wpa_s->max_sched_scan_plans) {
+               wpa_printf(MSG_WARNING,
+                          "Too many scheduled scan plans (only %u supported)",
+                          wpa_s->max_sched_scan_plans);
+               wpa_printf(MSG_WARNING,
+                          "Use only the first %u scan plans, and the last one (in infinite loop)",
+                          wpa_s->max_sched_scan_plans - 1);
+               os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+                         &scan_plans[num - 1], sizeof(*scan_plans));
+               num = wpa_s->max_sched_scan_plans;
+       }
+
+       os_free(wpa_s->sched_scan_plans);
+       wpa_s->sched_scan_plans = scan_plans;
+       wpa_s->sched_scan_plans_num = num;
+
+       return 0;
+
+fail:
+       os_free(scan_plans);
+       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);
+}