diff options
author | Arend van Spriel <arend@broadcom.com> | 2016-03-02 14:37:18 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-04-05 04:56:34 -0400 |
commit | 38de03d2a28925b489c11546804e2f5418cc17a4 (patch) | |
tree | dd54c49467ce3993f5e916862de528df873e984b /net/wireless | |
parent | f59374eb427fb1377fdb7b8b3691c48e0c77a3c4 (diff) |
nl80211: add feature for BSS selection support
Introducing a new feature that the driver can use to
indicate the driver/firmware supports configuration of BSS
selection criteria upon CONNECT command. This can be useful
when multiple BSS-es are found belonging to the same ESS,
ie. Infra-BSS with same SSID. The criteria can then be used to
offload selection of a preferred BSS.
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Lei Zhang <leizh@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
[move wiphy support check into parse_bss_select()]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 7 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 111 |
2 files changed, 118 insertions, 0 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 9f1c4aa851ef..5327e4b974fa 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -626,6 +626,13 @@ int wiphy_register(struct wiphy *wiphy) | |||
626 | !rdev->ops->set_mac_acl))) | 626 | !rdev->ops->set_mac_acl))) |
627 | return -EINVAL; | 627 | return -EINVAL; |
628 | 628 | ||
629 | /* assure only valid behaviours are flagged by driver | ||
630 | * hence subtract 2 as bit 0 is invalid. | ||
631 | */ | ||
632 | if (WARN_ON(wiphy->bss_select_support && | ||
633 | (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2)))) | ||
634 | return -EINVAL; | ||
635 | |||
629 | if (wiphy->addresses) | 636 | if (wiphy->addresses) |
630 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); | 637 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); |
631 | 638 | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1b43f7839eeb..d6c6449c0389 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -402,6 +402,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { | |||
402 | [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, | 402 | [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, |
403 | [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, | 403 | [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, |
404 | [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, | 404 | [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, |
405 | [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED }, | ||
405 | }; | 406 | }; |
406 | 407 | ||
407 | /* policy for the key attributes */ | 408 | /* policy for the key attributes */ |
@@ -486,6 +487,15 @@ nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = { | |||
486 | [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 }, | 487 | [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 }, |
487 | }; | 488 | }; |
488 | 489 | ||
490 | static const struct nla_policy | ||
491 | nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = { | ||
492 | [NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG }, | ||
493 | [NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 }, | ||
494 | [NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = { | ||
495 | .len = sizeof(struct nl80211_bss_select_rssi_adjust) | ||
496 | }, | ||
497 | }; | ||
498 | |||
489 | static int nl80211_prepare_wdev_dump(struct sk_buff *skb, | 499 | static int nl80211_prepare_wdev_dump(struct sk_buff *skb, |
490 | struct netlink_callback *cb, | 500 | struct netlink_callback *cb, |
491 | struct cfg80211_registered_device **rdev, | 501 | struct cfg80211_registered_device **rdev, |
@@ -1731,6 +1741,25 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, | |||
1731 | rdev->wiphy.ext_features)) | 1741 | rdev->wiphy.ext_features)) |
1732 | goto nla_put_failure; | 1742 | goto nla_put_failure; |
1733 | 1743 | ||
1744 | if (rdev->wiphy.bss_select_support) { | ||
1745 | struct nlattr *nested; | ||
1746 | u32 bss_select_support = rdev->wiphy.bss_select_support; | ||
1747 | |||
1748 | nested = nla_nest_start(msg, NL80211_ATTR_BSS_SELECT); | ||
1749 | if (!nested) | ||
1750 | goto nla_put_failure; | ||
1751 | |||
1752 | i = 0; | ||
1753 | while (bss_select_support) { | ||
1754 | if ((bss_select_support & 1) && | ||
1755 | nla_put_flag(msg, i)) | ||
1756 | goto nla_put_failure; | ||
1757 | i++; | ||
1758 | bss_select_support >>= 1; | ||
1759 | } | ||
1760 | nla_nest_end(msg, nested); | ||
1761 | } | ||
1762 | |||
1734 | /* done */ | 1763 | /* done */ |
1735 | state->split_start = 0; | 1764 | state->split_start = 0; |
1736 | break; | 1765 | break; |
@@ -5758,6 +5787,73 @@ static int validate_scan_freqs(struct nlattr *freqs) | |||
5758 | return n_channels; | 5787 | return n_channels; |
5759 | } | 5788 | } |
5760 | 5789 | ||
5790 | static bool is_band_valid(struct wiphy *wiphy, enum ieee80211_band b) | ||
5791 | { | ||
5792 | return b < IEEE80211_NUM_BANDS && wiphy->bands[b]; | ||
5793 | } | ||
5794 | |||
5795 | static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, | ||
5796 | struct cfg80211_bss_selection *bss_select) | ||
5797 | { | ||
5798 | struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1]; | ||
5799 | struct nlattr *nest; | ||
5800 | int err; | ||
5801 | bool found = false; | ||
5802 | int i; | ||
5803 | |||
5804 | /* only process one nested attribute */ | ||
5805 | nest = nla_data(nla); | ||
5806 | if (!nla_ok(nest, nla_len(nest))) | ||
5807 | return -EINVAL; | ||
5808 | |||
5809 | err = nla_parse(attr, NL80211_BSS_SELECT_ATTR_MAX, nla_data(nest), | ||
5810 | nla_len(nest), nl80211_bss_select_policy); | ||
5811 | if (err) | ||
5812 | return err; | ||
5813 | |||
5814 | /* only one attribute may be given */ | ||
5815 | for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) { | ||
5816 | if (attr[i]) { | ||
5817 | if (found) | ||
5818 | return -EINVAL; | ||
5819 | found = true; | ||
5820 | } | ||
5821 | } | ||
5822 | |||
5823 | bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID; | ||
5824 | |||
5825 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI]) | ||
5826 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI; | ||
5827 | |||
5828 | if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) { | ||
5829 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF; | ||
5830 | bss_select->param.band_pref = | ||
5831 | nla_get_u32(attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]); | ||
5832 | if (!is_band_valid(wiphy, bss_select->param.band_pref)) | ||
5833 | return -EINVAL; | ||
5834 | } | ||
5835 | |||
5836 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) { | ||
5837 | struct nl80211_bss_select_rssi_adjust *adj_param; | ||
5838 | |||
5839 | adj_param = nla_data(attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]); | ||
5840 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST; | ||
5841 | bss_select->param.adjust.band = adj_param->band; | ||
5842 | bss_select->param.adjust.delta = adj_param->delta; | ||
5843 | if (!is_band_valid(wiphy, bss_select->param.adjust.band)) | ||
5844 | return -EINVAL; | ||
5845 | } | ||
5846 | |||
5847 | /* user-space did not provide behaviour attribute */ | ||
5848 | if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID) | ||
5849 | return -EINVAL; | ||
5850 | |||
5851 | if (!(wiphy->bss_select_support & BIT(bss_select->behaviour))) | ||
5852 | return -EINVAL; | ||
5853 | |||
5854 | return 0; | ||
5855 | } | ||
5856 | |||
5761 | static int nl80211_parse_random_mac(struct nlattr **attrs, | 5857 | static int nl80211_parse_random_mac(struct nlattr **attrs, |
5762 | u8 *mac_addr, u8 *mac_addr_mask) | 5858 | u8 *mac_addr, u8 *mac_addr_mask) |
5763 | { | 5859 | { |
@@ -8001,6 +8097,21 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
8001 | return -EOPNOTSUPP; | 8097 | return -EOPNOTSUPP; |
8002 | } | 8098 | } |
8003 | 8099 | ||
8100 | if (info->attrs[NL80211_ATTR_BSS_SELECT]) { | ||
8101 | /* bss selection makes no sense if bssid is set */ | ||
8102 | if (connect.bssid) { | ||
8103 | kzfree(connkeys); | ||
8104 | return -EINVAL; | ||
8105 | } | ||
8106 | |||
8107 | err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT], | ||
8108 | wiphy, &connect.bss_select); | ||
8109 | if (err) { | ||
8110 | kzfree(connkeys); | ||
8111 | return err; | ||
8112 | } | ||
8113 | } | ||
8114 | |||
8004 | wdev_lock(dev->ieee80211_ptr); | 8115 | wdev_lock(dev->ieee80211_ptr); |
8005 | err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); | 8116 | err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); |
8006 | wdev_unlock(dev->ieee80211_ptr); | 8117 | wdev_unlock(dev->ieee80211_ptr); |