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