aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-02-05 15:48:51 +0530
committerMister Oyster <oysterized@gmail.com>2017-04-13 12:32:24 +0200
commitfa5735fd9060d1c90bd96fa9633d7ee5d5dbe651 (patch)
treebe48298871ae0b07a455b9f308e1753be94470da
parentb9ee3975365c18fb009abcd739b1b77078821a03 (diff)
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters, with the found BSS having to pass at least one of them to be passed to the host. When the RSSI attribute was added, however, this was broken and currently wpa_supplicant adds that attribute in its own matchset; however, it doesn't intend that to mean that anything that passes the RSSI filter should be passed to the host, instead it wants it to mean that everything needs to also have higher RSSI. This is semantically problematic because we have a list of filters like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which one should be OR'ed and which one AND'ed. To fix this, move the RSSI filter attribute into each matchset. As we need to stay backward compatible, treat a matchset with only the RSSI attribute as a "default RSSI filter" for all other matchsets, but only if there are other matchsets (an RSSI-only matchset by itself is still desirable.) To make driver implementation easier, keep a global min_rssi_thold for the entire request as well. The only affected driver is ath6kl. I found this when I looked into the code after Raja Mani submitted a patch fixing the n_match_sets calculation to disregard the RSSI, but that patch didn't address the semantic issue. Reported-by: Raja Mani <rmani@qti.qualcomm.com> Acked-by: Luciano Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git Git-commit: ea73cbce4e1fd93113301532ad98041b119bc85a [kseelam@codeaurora.org: intel wireless driver changes covered in original patch has dependency of other patches present in upstream kernel. So, only intel driver (iwlwifi) changes are skipped and also retained rssi_thold in cfg80211_sched_scan_request structure to retain backward compatibility] CRs-Fixed: 608887 Change-Id: Iffc3e2fca36340163d8ba289baaa992af41a53c8 Signed-off-by: Komal Seelam <kseelam@codeaurora.org> (cherry picked from commit d5b24231be76c54c5aefea222d40fac03fd7f4ee)
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c18
-rw-r--r--include/net/cfg80211.h10
-rw-r--r--net/wireless/nl80211.c72
3 files changed, 83 insertions, 17 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 5c9736a94..008b4d92b 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -3244,6 +3244,14 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
struct ath6kl_vif *vif = netdev_priv(dev);
u16 interval;
int ret, rssi_thold;
+ int n_match_sets = request->n_match_sets;
+
+ /* If there's a matchset w/o an SSID, then assume it's just for
+ * the RSSI (nothing else is currently supported) and ignore it.
+ * The device only supports a global RSSI filter that we set below.
+ */
+ if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len)
+ n_match_sets = 0;
if (ar->state != ATH6KL_STATE_ON)
return -EIO;
@@ -3256,11 +3264,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
request->n_ssids,
request->match_sets,
- request->n_match_sets);
+ n_match_sets);
if (ret < 0)
return ret;
- if (!request->n_match_sets) {
+ if (!n_match_sets) {
ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
ALL_BSS_FILTER, 0);
if (ret < 0)
@@ -3274,12 +3282,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD,
ar->fw_capabilities)) {
- if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
+ if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF)
rssi_thold = 0;
- else if (request->rssi_thold < -127)
+ else if (request->min_rssi_thold < -127)
rssi_thold = -127;
else
- rssi_thold = request->rssi_thold;
+ rssi_thold = request->min_rssi_thold;
ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx,
rssi_thold);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6719f3fe0..7957aa425 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1268,10 +1268,12 @@ struct cfg80211_scan_request {
/**
* struct cfg80211_match_set - sets of attributes to match
*
- * @ssid: SSID to be matched
+ * @ssid: SSID to be matched; may be zero-length for no match (RSSI only)
+ * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
*/
struct cfg80211_match_set {
struct cfg80211_ssid ssid;
+ s32 rssi_thold;
};
/**
@@ -1293,7 +1295,8 @@ struct cfg80211_match_set {
* @dev: the interface
* @scan_start: start time of the scheduled scan
* @channels: channels to scan
- * @rssi_thold: don't report scan results below this threshold (in s32 dBm)
+ * @min_rssi_thold: for drivers only supporting a single threshold, this
+ * contains the minimum over all matchsets
*/
struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids;
@@ -1305,7 +1308,8 @@ struct cfg80211_sched_scan_request {
u32 flags;
struct cfg80211_match_set *match_sets;
int n_match_sets;
- s32 rssi_thold;
+ s32 min_rssi_thold;
+ s32 rssi_thold; /* just for backward compatible */
/* internal */
struct wiphy *wiphy;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 53fa90a6b..7cd5224d5 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -5331,6 +5331,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
enum ieee80211_band band;
size_t ie_len;
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
+ s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_start)
@@ -5369,11 +5370,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (n_ssids > wiphy->max_sched_scan_ssids)
return -EINVAL;
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
+ /*
+ * First, count the number of 'real' matchsets. Due to an issue with
+ * the old implementation, matchsets containing only the RSSI attribute
+ * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
+ * RSSI for all matchsets, rather than their own matchset for reporting
+ * all APs with a strong RSSI. This is needed to be compatible with
+ * older userspace that treated a matchset with only the RSSI as the
+ * global RSSI for all other matchsets - if there are other matchsets.
+ */
+ if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
- tmp)
- n_match_sets++;
+ tmp) {
+ struct nlattr *rssi;
+
+ err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_match_policy);
+ if (err)
+ return err;
+ /* add other standalone attributes here */
+ if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
+ n_match_sets++;
+ continue;
+ }
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ default_match_rssi = nla_get_s32(rssi);
+ }
+ }
+
+ /* However, if there's no other matchset, add the RSSI one */
+ if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
+ n_match_sets = 1;
if (n_match_sets > wiphy->max_match_sets)
return -EINVAL;
@@ -5501,6 +5531,15 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
nl80211_match_policy);
ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
if (ssid) {
+ if (WARN_ON(i >= n_match_sets)) {
+ /* this indicates a programming error,
+ * the loop above should have verified
+ * things properly
+ */
+ err = -EINVAL;
+ goto out_free;
+ }
+
if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
goto out_free;
@@ -5509,17 +5548,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
nla_data(ssid), nla_len(ssid));
request->match_sets[i].ssid.ssid_len =
nla_len(ssid);
+ /* special attribute - old implemenation w/a */
+ request->match_sets[i].rssi_thold =
+ default_match_rssi;
+ rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+ if (rssi)
+ request->match_sets[i].rssi_thold =
+ nla_get_s32(rssi);
}
- rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
- if (rssi)
- request->rssi_thold = nla_get_u32(rssi);
- else
- request->rssi_thold =
- NL80211_SCAN_RSSI_THOLD_OFF;
i++;
}
+
+ /* there was no other matchset, so the RSSI one is alone */
+ if (i == 0)
+ request->match_sets[0].rssi_thold = default_match_rssi;
+
+ request->min_rssi_thold = INT_MAX;
+ for (i = 0; i < n_match_sets; i++)
+ request->min_rssi_thold =
+ min(request->match_sets[i].rssi_thold,
+ request->min_rssi_thold);
+ } else {
+ request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
}
+ request->rssi_thold = request->min_rssi_thold;
+
if (info->attrs[NL80211_ATTR_IE]) {
request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
memcpy((void *)request->ie,