184c335
diff -up wpa_supplicant-0.6.8/src/drivers/driver.h.ap-stability wpa_supplicant-0.6.8/src/drivers/driver.h
184c335
--- wpa_supplicant-0.6.8/src/drivers/driver.h.ap-stability	2009-02-15 13:00:00.000000000 -0500
184c335
+++ wpa_supplicant-0.6.8/src/drivers/driver.h	2009-05-12 16:01:32.000000000 -0400
184c335
@@ -1017,6 +1017,21 @@ struct wpa_driver_ops {
184c335
 	 * failure
184c335
 	 */
184c335
 	struct wpa_interface_info * (*get_interfaces)(void *global_priv);
184c335
+
184c335
+ 	/**
184c335
+	 * get_signal_quality - Request signal quality of the current association
184c335
+	 * @priv: private driver interface data
184c335
+	 * @qual: signal "quality", perhaps including TX errors, missed beacons,
184c335
+	 *        etc.  If not provided, set to 0.
184c335
+	 * @max_qual: maximum possible signal quality.  If not provided set to 0.
184c335
+	 *
184c335
+	 * This handler may be called at any time to retrieve the signal quality
184c335
+	 * of the current association.  If there is no association, the handler
184c335
+	 * must return -1.  If the signal level isn't known or is not provided,
184c335
+	 * the handler must return -1.
184c335
+	 * Returns: 0 on success, -1 on failure
184c335
+	 */
184c335
+	int (*get_signal_quality)(void *priv, int *qual, int *max_qual);
184c335
 };
184c335
 
184c335
 /* Function to check whether a driver is for wired connections */
184c335
diff -up wpa_supplicant-0.6.8/src/drivers/driver_wext.c.ap-stability wpa_supplicant-0.6.8/src/drivers/driver_wext.c
184c335
--- wpa_supplicant-0.6.8/src/drivers/driver_wext.c.ap-stability	2009-05-12 16:01:32.000000000 -0400
184c335
+++ wpa_supplicant-0.6.8/src/drivers/driver_wext.c	2009-05-12 16:01:32.000000000 -0400
184c335
@@ -1615,6 +1615,10 @@ static int wpa_driver_wext_get_range(voi
184c335
 		if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
184c335
 			drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
184c335
 
184c335
+		drv->max_qual.qual = range->max_qual.qual;
184c335
+		drv->max_qual.level = range->max_qual.level;
184c335
+		drv->max_qual.updated = range->max_qual.updated;
184c335
+
184c335
 		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
184c335
 			   "flags 0x%x",
184c335
 			   drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
184c335
@@ -2244,6 +2248,51 @@ done:
184c335
 }
184c335
 
184c335
 
184c335
+/**
184c335
+ * wpa_driver_wext_get_signal_quality - Get wireless signal quality, SIOCSIWSTATS
184c335
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
184c335
+ * @qual: signal quality
184c335
+ * @max_qual: maximum signal quality
184c335
+ * Returns: 0 on success, -1 on failure
184c335
+ */
184c335
+int wpa_driver_wext_get_signal_quality(void *priv, int *qual, int *max_qual)
184c335
+{
184c335
+	struct wpa_driver_wext_data *drv = priv;
184c335
+	struct iwreq iwr;
184c335
+	struct iw_statistics stats;
184c335
+	s8 level = 0;
184c335
+
184c335
+	os_memset(&iwr, 0, sizeof(iwr));
184c335
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
184c335
+
184c335
+	memset (&stats, 0, sizeof (stats));
184c335
+	iwr.u.data.pointer = &stat;;
184c335
+	iwr.u.data.length = sizeof (stats);
184c335
+	iwr.u.data.flags = 1;		/* Clear updated flag */
184c335
+
184c335
+	if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr)) {
184c335
+		wpa_printf(MSG_DEBUG, "%s: IWSTATS returned error: %d",
184c335
+		           __FUNCTION__, errno);
184c335
+		return -1;
184c335
+	}
184c335
+
184c335
+	if (   !(drv->max_qual.updated & IW_QUAL_LEVEL_INVALID)
184c335
+	    && !(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
184c335
+		level = stats.qual.level;
184c335
+	}
184c335
+
184c335
+	if (   !(drv->max_qual.updated & IW_QUAL_QUAL_INVALID)
184c335
+	    && !(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
184c335
+		*qual = stats.qual.qual;
184c335
+		*max_qual = drv->max_qual.qual;
184c335
+	}
184c335
+
184c335
+	wpa_printf(MSG_DEBUG, "%s: level %d (%d), qual %d (%d)", __FUNCTION__,
184c335
+	           level, drv->max_qual.level, *qual, drv->max_qual.qual);
184c335
+	return 0;
184c335
+}
184c335
+
184c335
+
184c335
 static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
184c335
 				 u32 cmd, const u8 *bssid, const u8 *pmkid)
184c335
 {
184c335
@@ -2357,6 +2406,7 @@ const struct wpa_driver_ops wpa_driver_w
184c335
 	.deauthenticate = wpa_driver_wext_deauthenticate,
184c335
 	.disassociate = wpa_driver_wext_disassociate,
184c335
 	.set_mode = wpa_driver_wext_set_mode,
184c335
+	.get_signal_quality = wpa_driver_wext_get_signal_quality,
184c335
 	.associate = wpa_driver_wext_associate,
184c335
 	.set_auth_alg = wpa_driver_wext_set_auth_alg,
184c335
 	.init = wpa_driver_wext_init,
184c335
diff -up wpa_supplicant-0.6.8/src/drivers/driver_wext.h.ap-stability wpa_supplicant-0.6.8/src/drivers/driver_wext.h
184c335
--- wpa_supplicant-0.6.8/src/drivers/driver_wext.h.ap-stability	2009-02-15 13:00:00.000000000 -0500
184c335
+++ wpa_supplicant-0.6.8/src/drivers/driver_wext.h	2009-05-12 16:01:32.000000000 -0400
184c335
@@ -16,6 +16,7 @@
184c335
 #define DRIVER_WEXT_H
184c335
 
184c335
 #include <net/if.h>
3da3dbf
+#include "wireless_copy.h"
184c335
 
184c335
 struct wpa_driver_wext_data {
184c335
 	void *ctx;
184c335
@@ -43,6 +44,8 @@ struct wpa_driver_wext_data {
184c335
 	char mlmedev[IFNAMSIZ + 1];
184c335
 
184c335
 	int scan_complete_events;
184c335
+
184c335
+	struct iw_quality max_qual;
184c335
 };
184c335
 
184c335
 int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags);
184c335
diff -up wpa_supplicant-0.6.8/wpa_supplicant/events.c.ap-stability wpa_supplicant-0.6.8/wpa_supplicant/events.c
184c335
--- wpa_supplicant-0.6.8/wpa_supplicant/events.c.ap-stability	2009-05-12 16:01:32.000000000 -0400
184c335
+++ wpa_supplicant-0.6.8/wpa_supplicant/events.c	2009-05-12 16:19:06.000000000 -0400
184c335
@@ -371,9 +371,53 @@ static int wpa_supplicant_ssid_bss_match
184c335
 }
184c335
 
184c335
 
184c335
+struct cur_ap {
184c335
+	u8 bssid[ETH_ALEN];
184c335
+	int qual;
184c335
+	int max_qual;
184c335
+};
184c335
+
184c335
+#define CUR_AP_THRESHOLD 50
184c335
+
184c335
+/* Return 1 if 'bss' should be used instead of the current association */
184c335
+static int
184c335
+bss_better_quality(struct wpa_scan_res *bss, struct cur_ap *cur)
184c335
+{
184c335
+	int cur_pqual, bss_pqual;
184c335
+
184c335
+	/* If the max quality is invalid, quality is pretty meaningless */
184c335
+	if (!cur->max_qual) {
184c335
+		wpa_printf(MSG_DEBUG, "   no max quality");
184c335
+		return 1;
184c335
+	}
184c335
+
184c335
+	cur_pqual = (int) (((float) cur->qual / (float) cur->max_qual) * 100);
184c335
+	bss_pqual = (int) (((float) bss->qual / (float) cur->max_qual) * 100);
184c335
+
184c335
+	/* If 'bss' is the current associated BSS and it's still got OK quality,
184c335
+	 * stick with it.
184c335
+	 */
184c335
+	if (!os_memcmp(cur->bssid, bss->bssid, ETH_ALEN) && (cur_pqual >= CUR_AP_THRESHOLD)) {
184c335
+		wpa_printf(MSG_DEBUG, "   matched associated BSSID");
184c335
+		return 1;
184c335
+	}
184c335
+
184c335
+	wpa_printf(MSG_DEBUG, "     cur AP qual: %d  candidate qual: %d", cur_pqual, bss_pqual);
184c335
+
184c335
+	/* Otherwise if the current association is worse than 50% quality and
184c335
+	 * 'bss' is at least 15% better, then use 'bss'.
184c335
+	 */
184c335
+	if ((cur_pqual < CUR_AP_THRESHOLD) && (bss_pqual >= cur_pqual + 15))
184c335
+		return 1;
184c335
+
184c335
+	return 0;
184c335
+}
184c335
+
184c335
+
184c335
 static struct wpa_scan_res *
184c335
 wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
184c335
 			      struct wpa_ssid *group,
184c335
+			      struct cur_ap *cur,
184c335
 			      struct wpa_ssid **selected_ssid)
184c335
 {
184c335
 	struct wpa_ssid *ssid;
184c335
@@ -448,6 +492,12 @@ wpa_supplicant_select_bss_wpa(struct wpa
184c335
 			if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
184c335
 				continue;
184c335
 
184c335
+			if (cur && !bss_better_quality(bss, cur)) {
184c335
+				wpa_printf(MSG_DEBUG, "   skip - "
184c335
+					   "signal strength not high enough");
184c335
+				continue;
184c335
+			}
184c335
+
184c335
 			wpa_printf(MSG_DEBUG, "   selected WPA AP "
184c335
 				   MACSTR " ssid='%s'",
184c335
 				   MAC2STR(bss->bssid),
184c335
@@ -464,6 +514,7 @@ wpa_supplicant_select_bss_wpa(struct wpa
184c335
 static struct wpa_scan_res *
184c335
 wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
184c335
 				  struct wpa_ssid *group,
184c335
+			          struct cur_ap *cur,
184c335
 				  struct wpa_ssid **selected_ssid)
184c335
 {
184c335
 	struct wpa_ssid *ssid;
184c335
@@ -569,6 +620,12 @@ wpa_supplicant_select_bss_non_wpa(struct
184c335
 				continue;
184c335
 			}
184c335
 
184c335
+			if (cur && !bss_better_quality(bss, cur)) {
184c335
+				wpa_printf(MSG_DEBUG, "   skip - "
184c335
+					   "signal strength not high enough");
184c335
+				continue;
184c335
+			}
184c335
+
184c335
 			wpa_printf(MSG_DEBUG, "   selected non-WPA AP "
184c335
 				   MACSTR " ssid='%s'",
184c335
 				   MAC2STR(bss->bssid),
184c335
@@ -584,21 +641,45 @@ wpa_supplicant_select_bss_non_wpa(struct
184c335
 
184c335
 static struct wpa_scan_res *
184c335
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
184c335
-			  struct wpa_ssid **selected_ssid)
184c335
+			  struct cur_ap *cur, struct wpa_ssid **selected_ssid)
184c335
 {
184c335
 	struct wpa_scan_res *selected;
184c335
 
184c335
 	wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
184c335
 		   group->priority);
184c335
 
184c335
+	if (cur) {
184c335
+		int found = 0, i;
184c335
+		struct wpa_scan_res *bss;
184c335
+
184c335
+		wpa_printf(MSG_DEBUG, "Try to find current BSSID "
184c335
+		           "%02x:%02x:%02x:%02x:%02x:%02x",
184c335
+		           cur->bssid[0], cur->bssid[1], cur->bssid[2],
184c335
+		           cur->bssid[3], cur->bssid[4], cur->bssid[5]);
184c335
+		for (i = 0; i < wpa_s->scan_res->num; i++) {
184c335
+			bss = wpa_s->scan_res->res[i];
184c335
+			if (os_memcmp(bss->bssid, cur->bssid, ETH_ALEN) != 0) {
184c335
+				wpa_printf(MSG_DEBUG, "   skip - "
184c335
+					   "BSSID mismatch");
184c335
+				continue;
184c335
+			}
184c335
+			wpa_printf(MSG_DEBUG, "   found");
184c335
+			found = 1;
184c335
+			break;
184c335
+		}
184c335
+
184c335
+		if (!found)
184c335
+			cur = NULL;
184c335
+	}
184c335
+
184c335
 	/* First, try to find WPA-enabled AP */
184c335
-	selected = wpa_supplicant_select_bss_wpa(wpa_s, group, selected_ssid);
184c335
+	selected = wpa_supplicant_select_bss_wpa(wpa_s, group, cur, selected_ssid);
184c335
 	if (selected)
184c335
 		return selected;
184c335
 
184c335
 	/* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
184c335
 	 * allows this. */
184c335
-	return wpa_supplicant_select_bss_non_wpa(wpa_s, group, selected_ssid);
184c335
+	return wpa_supplicant_select_bss_non_wpa(wpa_s, group, cur, selected_ssid);
184c335
 }
184c335
 
184c335
 
184c335
@@ -607,6 +687,8 @@ static void wpa_supplicant_event_scan_re
184c335
 	int prio, timeout;
184c335
 	struct wpa_scan_res *selected = NULL;
184c335
 	struct wpa_ssid *ssid = NULL;
184c335
+	int qual = 0, max_qual = 0, qual_valid = 0, bssid_valid = 0, i;
184c335
+	struct cur_ap cur;
184c335
 
184c335
 	if (wpa_supplicant_get_scan_results(wpa_s) < 0) {
184c335
 		if (wpa_s->conf->ap_scan == 2)
184c335
@@ -635,10 +717,44 @@ static void wpa_supplicant_event_scan_re
184c335
 	    wpa_s->disconnected)
184c335
 		return;
184c335
 
184c335
+	/* Get current driver BSSID and signal strength */
184c335
+	os_memset(&cur, 0, sizeof(cur));
184c335
+
184c335
+	for (i = 0; i < 4; i++) {
184c335
+		static u8 bad1[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
184c335
+		static u8 bad2[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
184c335
+		static u8 bad3[ETH_ALEN] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44};
184c335
+
184c335
+		if (wpa_drv_get_bssid(wpa_s, (u8 *) &cur.bssid) == 0) {
184c335
+			if (memcmp(cur.bssid, bad1, ETH_ALEN) &&
184c335
+			    memcmp(cur.bssid, bad2, ETH_ALEN) &&
184c335
+			    memcmp(cur.bssid, bad3, ETH_ALEN)) {
184c335
+				bssid_valid = 1;
184c335
+				break;
184c335
+			}
184c335
+		}
184c335
+	}
184c335
+
184c335
+	if (bssid_valid) {
184c335
+		for (i = 0; i < 4; i++) {
184c335
+			qual_valid = !wpa_drv_get_signal_quality(wpa_s, &qual, &max_qual);
184c335
+			if (qual_valid && qual) {
184c335
+				cur.qual = qual;
184c335
+				cur.max_qual = max_qual;
184c335
+				break;
184c335
+			}
184c335
+		}
184c335
+	}
184c335
+
184c335
+	wpa_printf(MSG_DEBUG, "%s: qual %d (%d)  qv=%d  bv=%d",
184c335
+		   __FUNCTION__, qual, max_qual, qual_valid, bssid_valid);
184c335
+
184c335
 	while (selected == NULL) {
184c335
 		for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
184c335
 			selected = wpa_supplicant_select_bss(
184c335
-				wpa_s, wpa_s->conf->pssid[prio], &ssid);
184c335
+				wpa_s, wpa_s->conf->pssid[prio],
184c335
+				(bssid_valid && qual_valid) ? &cur : NULL,
184c335
+				&ssid);
184c335
 			if (selected)
184c335
 				break;
184c335
 		}
184c335
diff -up wpa_supplicant-0.6.8/wpa_supplicant/wpa_supplicant_i.h.ap-stability wpa_supplicant-0.6.8/wpa_supplicant/wpa_supplicant_i.h
184c335
--- wpa_supplicant-0.6.8/wpa_supplicant/wpa_supplicant_i.h.ap-stability	2009-05-12 16:01:32.000000000 -0400
184c335
+++ wpa_supplicant-0.6.8/wpa_supplicant/wpa_supplicant_i.h	2009-05-12 16:01:33.000000000 -0400
184c335
@@ -485,6 +485,15 @@ static inline int wpa_drv_set_mode(struc
184c335
 	return 0;
184c335
 }
184c335
 
184c335
+static inline int wpa_drv_get_signal_quality(struct wpa_supplicant *wpa_s,
184c335
+	int *qual, int *max_qual)
184c335
+{
184c335
+	if (wpa_s->driver->get_signal_quality) {
184c335
+		return wpa_s->driver->get_signal_quality(wpa_s->drv_priv, qual, max_qual);
184c335
+	}
184c335
+	return -1;
184c335
+}
184c335
+
184c335
 static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
184c335
 				    struct wpa_driver_associate_params *params)
184c335
 {