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:
parent
fbc72d32c6
commit
577db0aedd
2 changed files with 118 additions and 7 deletions
|
@ -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,
|
static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
|
||||||
struct nlattr *tb[])
|
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);
|
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
|
||||||
msg = NULL;
|
msg = NULL;
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
|
wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
|
||||||
(unsigned long) res->num);
|
"BSSes)", (unsigned long) res->num);
|
||||||
|
nl80211_get_noise_for_scan_results(drv, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
|
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
|
||||||
|
|
|
@ -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
|
/* Compare function for sorting scan results. Return >0 if @b is considered
|
||||||
* better. */
|
* better. */
|
||||||
static int wpa_scan_result_compar(const void *a, const void *b)
|
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 **_wa = (void *) a;
|
||||||
struct wpa_scan_res **_wb = (void *) b;
|
struct wpa_scan_res **_wb = (void *) b;
|
||||||
struct wpa_scan_res *wa = *_wa;
|
struct wpa_scan_res *wa = *_wa;
|
||||||
struct wpa_scan_res *wb = *_wb;
|
struct wpa_scan_res *wb = *_wb;
|
||||||
int wpa_a, wpa_b, maxrate_a, maxrate_b;
|
int wpa_a, wpa_b, maxrate_a, maxrate_b;
|
||||||
|
int snr_a, snr_b;
|
||||||
|
|
||||||
/* WPA/WPA2 support preferred */
|
/* WPA/WPA2 support preferred */
|
||||||
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
|
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)
|
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* best/max rate preferred if signal level close enough XXX */
|
if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
|
||||||
if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
|
!((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)) {
|
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
|
||||||
maxrate_a = wpa_scan_get_max_rate(wa);
|
maxrate_a = wpa_scan_get_max_rate(wa);
|
||||||
maxrate_b = wpa_scan_get_max_rate(wb);
|
maxrate_b = wpa_scan_get_max_rate(wb);
|
||||||
if (maxrate_a != maxrate_b)
|
if (maxrate_a != maxrate_b)
|
||||||
return maxrate_b - maxrate_a;
|
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 */
|
/* 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
|
* identical, use quality values since some drivers may only report
|
||||||
* that value and leave the signal level zero */
|
* 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->qual - wa->qual;
|
||||||
return wb->level - wa->level;
|
return snr_b - snr_a;
|
||||||
|
#undef MIN
|
||||||
|
#undef IS_5GHZ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue