Prefer 5 GHz networks over 2.4 GHz networks

In scan.c, merge a channel's noise value into the scan results. When
comparing scan results, compute the signal-to-noise ratio and use it
when available. Prefer a 5 GHz network if its SNR is really big (> 30)
or if its SNR is relatively close to the other network's.
This commit is contained in:
Gary Morain 2011-08-05 16:23:12 -07:00 committed by Jouni Malinen
parent fbc72d32c6
commit 577db0aedd
2 changed files with 118 additions and 7 deletions

View file

@ -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 "

View file

@ -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
}