Add a scan result cache to improve roaming speed if the driver gave us a background scan before losing the connection.

--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -97,6 +97,12 @@
 	int ap_scan;
 
 	/**
+	 * scan_cache - controls the time in seconds after the last scan results
+	 * before a new scan may be initiated
+	 */
+	int scan_cache;
+
+	/**
 	 * ctrl_interface - Parameters for the control interface
 	 *
 	 * If this is specified, %wpa_supplicant will open a control interface
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -312,6 +312,13 @@
 	return 0;
 }
 
+static int wpa_config_process_scan_cache(struct wpa_config *config, char *pos)
+{
+	config->scan_cache = atoi(pos);
+	wpa_printf(MSG_DEBUG, "scan_cache=%d", config->scan_cache);
+	return 0;
+}
+
 
 static int wpa_config_process_fast_reauth(struct wpa_config *config, char *pos)
 {
@@ -445,6 +452,9 @@
 	if (os_strncmp(pos, "ap_scan=", 8) == 0)
 		return wpa_config_process_ap_scan(config, pos + 8);
 
+	if (os_strncmp(pos, "scan_cache=", 11) == 0)
+		return wpa_config_process_scan_cache(config, pos + 11);
+
 	if (os_strncmp(pos, "fast_reauth=", 12) == 0)
 		return wpa_config_process_fast_reauth(config, pos + 12);
 
@@ -810,6 +820,8 @@
 		fprintf(f, "eapol_version=%d\n", config->eapol_version);
 	if (config->ap_scan != DEFAULT_AP_SCAN)
 		fprintf(f, "ap_scan=%d\n", config->ap_scan);
+	if (config->scan_cache != 0)
+		fprintf(f, "scan_cache=%d\n", config->scan_cache);
 	if (config->fast_reauth != DEFAULT_FAST_REAUTH)
 		fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
 #ifdef EAP_TLS_OPENSSL
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -555,6 +555,9 @@
 	if (wpa_s->conf->ap_scan == 2 || wpa_s->disconnected)
 		return;
 
+	if (wpa_s->wpa_state > WPA_ASSOCIATED)
+		goto done;
+
 	while (selected == NULL) {
 		for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
 			selected = wpa_supplicant_select_bss(
@@ -567,6 +570,7 @@
 			wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
 				   "and try again");
 			wpa_blacklist_clear(wpa_s);
+			memset(&wpa_s->last_scan_results, 0, sizeof(wpa_s->last_scan_results));
 		} else if (selected == NULL) {
 			break;
 		}
@@ -594,10 +598,12 @@
 		rsn_preauth_scan_results(wpa_s->wpa, wpa_s->scan_res);
 	} else {
 		wpa_printf(MSG_DEBUG, "No suitable AP found.");
-		timeout = 5;
+		timeout = 0;
 		goto req_scan;
 	}
 
+done:
+	os_get_time(&wpa_s->last_scan_results);
 	return;
 
 req_scan:
@@ -804,6 +810,9 @@
 	}
 	if (wpa_s->wpa_state >= WPA_ASSOCIATED)
 		wpa_supplicant_req_scan(wpa_s, 0, 100000);
+	else if (wpa_s->wpa_state == WPA_ASSOCIATING)
+		wpa_supplicant_req_auth_timeout(wpa_s, 0, 100000);
+
 	bssid = wpa_s->bssid;
 	if (os_memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0)
 		bssid = wpa_s->pending_bssid;
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -334,6 +334,7 @@
 	struct wpa_client_mlme mlme;
 	int use_client_mlme;
 	int driver_4way_handshake;
+	struct os_time last_scan_results;
 };
 
 
@@ -381,6 +382,7 @@
 
 /* scan.c */
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_may_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
 
 /* events.c */
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -40,6 +40,18 @@
 	wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
 }
 
+int wpa_supplicant_may_scan(struct wpa_supplicant *wpa_s)
+{
+	struct os_time time;
+
+	if (wpa_s->conf->scan_cache > 0) {
+		os_get_time(&time);
+		time.sec -= wpa_s->conf->scan_cache;
+		if (os_time_before(&time, &wpa_s->last_scan_results))
+			return 0;
+	}
+	return 1;
+}
 
 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 {
@@ -150,8 +162,9 @@
 	} else
 		wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
 
-	if (wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 &&
-	    !wpa_s->use_client_mlme) {
+	if (!wpa_supplicant_may_scan(wpa_s) ||
+		(wpa_s->scan_res_tried == 0 && wpa_s->conf->ap_scan == 1 &&
+	    !wpa_s->use_client_mlme)) {
 		wpa_s->scan_res_tried++;
 		wpa_printf(MSG_DEBUG, "Trying to get current scan results "
 			   "first without requesting a new scan to speed up "
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1417,6 +1417,9 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	if (wpa_s->wpa_state < WPA_ASSOCIATING)
+		return;
+
 	wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
 	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
