diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 8fc6dd9bf..2ab10aef1 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1413,6 +1413,80 @@ static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, } +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); + nla_put_failure: + return -ENOBUFS; +} + + static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { @@ -3129,8 +3203,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { - wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", - (unsigned long) res->num); + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 0f5335e93..bb3fa86dc 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -985,15 +985,28 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( } +/* + * Channels with a great SNR can operate at full rate. What is a great SNR? + * 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. + */ +#define GREAT_SNR 30 + /* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { +#define IS_5GHZ(n) (n > 4000) +#define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b, maxrate_a, maxrate_b; + int snr_a, snr_b; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || @@ -1014,23 +1027,46 @@ static int wpa_scan_result_compar(const void *a, const void *b) (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; - /* best/max rate preferred if signal level close enough XXX */ - if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || + if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) && + !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) { + snr_a = MIN(wa->level - wa->noise, GREAT_SNR); + snr_b = MIN(wb->level - wb->noise, GREAT_SNR); + } else { + /* Not suitable information to calculate SNR, so use level */ + snr_a = wa->level; + snr_b = wb->level; + } + + wpa_printf(MSG_EXCESSIVE, "BSS(a) " MACSTR " freq:%d level:%d " + "noise:%d snr:%d flags:0x%x", + MAC2STR(wa->bssid), wa->freq, wa->level, wa->noise, snr_a, + wa->flags); + wpa_printf(MSG_EXCESSIVE, "BSS(b) " MACSTR " freq:%d level:%d " + "noise:%d snr:%d flags:0x%x", + MAC2STR(wb->bssid), wb->freq, wb->level, wb->noise, snr_b, + wb->flags); + + /* best/max rate preferred if SNR close enough */ + if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); if (maxrate_a != maxrate_b) return maxrate_b - maxrate_a; + if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) + return IS_5GHZ(wa->freq) ? -1 : 1; } /* use freq for channel preference */ - /* all things being equal, use signal level; if signal levels are + /* all things being equal, use SNR; if SNRs are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ - if (wb->level == wa->level) + if (snr_b == snr_a) return wb->qual - wa->qual; - return wb->level - wa->level; + return snr_b - snr_a; +#undef MIN +#undef IS_5GHZ }