From dfb89c56add259b72a9c68d6b2846c1cd8c4e4b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Jun 2012 09:23:48 +0200 Subject: cfg80211: don't allow WoWLAN support without CONFIG_PM When CONFIG_PM is disabled, no device can possibly support WoWLAN since it can't go to sleep to start with. Due to this, mac80211 had even rejected the hardware registration. By making all the code and data for WoWLAN depend on CONFIG_PM we can promote this runtime error to a compile-time error. Add #ifdef around all WoWLAN code to remove it in systems that don't need it as they never suspend. Cc: Kalle Valo Acked-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/wireless/core.c | 4 ++++ net/wireless/nl80211.c | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index 907f62c80e28..ddd32afa5f0a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -421,9 +421,11 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; +#ifdef CONFIG_PM if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; +#endif if (WARN_ON(wiphy->ap_sme_capa && !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) @@ -500,12 +502,14 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } +#ifdef CONFIG_PM if (rdev->wiphy.wowlan.n_patterns) { if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || rdev->wiphy.wowlan.pattern_min_len > rdev->wiphy.wowlan.pattern_max_len)) return -EINVAL; } +#endif /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 234ff3bbd104..067c9fe02a7f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1112,6 +1112,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_ifs); } +#ifdef CONFIG_PM if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { struct nlattr *nl_wowlan; @@ -1152,6 +1153,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_wowlan); } +#endif if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, dev->wiphy.software_iftypes)) @@ -6276,6 +6278,7 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) return cfg80211_leave_mesh(rdev, dev); } +#ifdef CONFIG_PM static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6504,6 +6507,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) kfree(new_triggers.patterns); return err; } +#endif static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) { @@ -7158,6 +7162,7 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, +#ifdef CONFIG_PM { .cmd = NL80211_CMD_GET_WOWLAN, .doit = nl80211_get_wowlan, @@ -7174,6 +7179,7 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, +#endif { .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, .doit = nl80211_set_rekey_data, -- cgit v1.2.3 From bf0c111ec80355ee9fe2e2bdb609a536b54768d8 Mon Sep 17 00:00:00 2001 From: Mahesh Palivela Date: Fri, 22 Jun 2012 07:27:46 +0000 Subject: cfg80211: allow advertising VHT capabilities Allow drivers to advertise their VHT capabilities and export them to userspace via nl80211. Signed-off-by: Mahesh Palivela Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 6 ++++++ include/net/cfg80211.h | 17 +++++++++++++++++ net/wireless/nl80211.c | 9 +++++++++ 3 files changed, 32 insertions(+) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c0fc5d277338..23003272c70e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1813,6 +1813,9 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -1826,6 +1829,9 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7d3cd3ce9a26..1fc89c4f930c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -210,6 +210,22 @@ struct ieee80211_sta_ht_cap { struct ieee80211_mcs_info mcs; }; +/** + * struct ieee80211_sta_vht_cap - STA's VHT capabilities + * + * This structure describes most essential parameters needed + * to describe 802.11ac VHT capabilities for an STA. + * + * @vht_supported: is VHT supported by the STA + * @cap: VHT capabilities map as described in 802.11ac spec + * @vht_mcs: Supported VHT MCS rates + */ +struct ieee80211_sta_vht_cap { + bool vht_supported; + u32 cap; /* use IEEE80211_VHT_CAP_ */ + struct ieee80211_vht_mcs_info vht_mcs; +}; + /** * struct ieee80211_supported_band - frequency band definition * @@ -233,6 +249,7 @@ struct ieee80211_supported_band { int n_channels; int n_bitrates; struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; }; /* diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 067c9fe02a7f..5c4a720f0442 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -921,6 +921,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.bands[band]->ht_cap.ampdu_density))) goto nla_put_failure; + /* add VHT info */ + if (dev->wiphy.bands[band]->vht_cap.vht_supported && + (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET, + sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs), + &dev->wiphy.bands[band]->vht_cap.vht_mcs) || + nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA, + dev->wiphy.bands[band]->vht_cap.cap))) + goto nla_put_failure; + /* add frequencies */ nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); if (!nl_freqs) -- cgit v1.2.3 From 60771780c27cbc93d0b78da0c7fd7a8a540b029e Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:56 +0200 Subject: cfg80211: introduce cfg80211_stop_ap This functionality will be reused when interface is going down. Avoids code duplication. Also adds missing wdev locking. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/Makefile | 2 +- net/wireless/ap.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 4 ++++ net/wireless/nl80211.c | 17 +---------------- 4 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 net/wireless/ap.c (limited to 'net/wireless') diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 55a28ab21db9..0f7e0d621ab0 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o diff --git a/net/wireless/ap.c b/net/wireless/ap.c new file mode 100644 index 000000000000..45199cca63d5 --- /dev/null +++ b/net/wireless/ap.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "nl80211.h" +#include "core.h" + + +static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (!rdev->ops->stop_ap) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!wdev->beacon_interval) + return -ENOENT; + + err = rdev->ops->stop_ap(&rdev->wiphy, dev); + if (!err) + wdev->beacon_interval = 0; + + return err; +} + +int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_stop_ap(rdev, dev); + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless/core.h b/net/wireless/core.h index 609a579255ac..fef476d2117e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -289,6 +289,10 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, int freq, enum nl80211_channel_type channel_type); +/* AP */ +int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5c4a720f0442..20d0fd6d1286 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2521,23 +2521,8 @@ static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - if (!rdev->ops->stop_ap) - return -EOPNOTSUPP; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - if (!wdev->beacon_interval) - return -ENOENT; - - err = rdev->ops->stop_ap(&rdev->wiphy, dev); - if (!err) - wdev->beacon_interval = 0; - return err; + return cfg80211_stop_ap(rdev, dev); } static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { -- cgit v1.2.3 From ac800140c20e7ae51117e71289065bedd4930fc2 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:57 +0200 Subject: cfg80211: .stop_ap when interface is going down We'll need this for proper channel tracking (which is going to be needed for channel context accounting and finding matching/active interface combination). Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/core.c | 3 +++ net/wireless/util.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index ddd32afa5f0a..c65f59c952c9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -810,6 +810,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, case NL80211_IFTYPE_MESH_POINT: cfg80211_leave_mesh(rdev, dev); break; + case NL80211_IFTYPE_AP: + cfg80211_stop_ap(rdev, dev); + break; default: break; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 316cfd00914f..fc948d0a53f3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -814,6 +814,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, dev->ieee80211_ptr->mesh_id_up_len = 0; switch (otype) { + case NL80211_IFTYPE_AP: + cfg80211_stop_ap(rdev, dev); + break; case NL80211_IFTYPE_ADHOC: cfg80211_leave_ibss(rdev, dev, false); break; -- cgit v1.2.3 From f4489ebeffa436c8427a20e2f05004e783708cde Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:58 +0200 Subject: cfg80211: add channel tracking for AP and mesh We need to know which channel is used by a running AP and mesh for channel context accounting and finding matching/active interface combination. STA/IBSS have current_bss already which allows us to check which channel a vif is tuned to. Non-fixed channel IBSS can be handled with additional changes. Monitor mode is going to be handled differently. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ net/wireless/ap.c | 4 +++- net/wireless/mesh.c | 18 ++++++++++++++---- net/wireless/mlme.c | 1 + net/wireless/nl80211.c | 1 + 5 files changed, 22 insertions(+), 5 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1fc89c4f930c..c62bc7864adf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2408,6 +2408,9 @@ struct wireless_dev { struct ieee80211_channel *preset_chan; enum nl80211_channel_type preset_chantype; + /* for AP and mesh channel tracking */ + struct ieee80211_channel *channel; + bool ps; int ps_timeout; diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 45199cca63d5..fcc60d8dbefa 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -24,8 +24,10 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return -ENOENT; err = rdev->ops->stop_ap(&rdev->wiphy, dev); - if (!err) + if (!err) { wdev->beacon_interval = 0; + wdev->channel = NULL; + } return err; } diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3b73b07486cf..bab381344723 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -159,6 +159,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; + wdev->channel = setup->channel; } return err; @@ -184,6 +185,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, enum nl80211_channel_type channel_type) { struct ieee80211_channel *channel; + int err; channel = rdev_freq_to_chan(rdev, freq, channel_type); if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, @@ -205,9 +207,14 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return -ENETDOWN; - return rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, - wdev->netdev, - channel); + + err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, + wdev->netdev, + channel); + if (!err) + wdev->channel = channel; + + return err; } if (wdev->mesh_id_len) @@ -249,8 +256,11 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, return -ENOTCONN; err = rdev->ops->leave_mesh(&rdev->wiphy, dev); - if (!err) + if (!err) { wdev->mesh_id_len = 0; + wdev->channel = NULL; + } + return err; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index da4406f11929..a7882eb8c46e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -947,6 +947,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, if (WARN_ON(!chan)) goto out; + wdev->channel = chan; nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); out: wdev_unlock(wdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 20d0fd6d1286..12096b4ebf62 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2488,6 +2488,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->preset_chan = params.channel; wdev->preset_chantype = params.channel_type; wdev->beacon_interval = params.beacon_interval; + wdev->channel = params.channel; } return err; } -- cgit v1.2.3 From c30a3d38689bc601e03d5f2ad3c37d8ea13e46ca Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:59 +0200 Subject: cfg80211: track ibss fixed channel IBSS may hop between channels. It is necessary to account this special case when considering interface combinations. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/ibss.c | 1 + 2 files changed, 3 insertions(+) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c62bc7864adf..e030c6af86dd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2411,6 +2411,8 @@ struct wireless_dev { /* for AP and mesh channel tracking */ struct ieee80211_channel *channel; + bool ibss_fixed; + bool ps; int ps_timeout; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 89baa3328411..b90fd86b2d18 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -113,6 +113,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, kfree(wdev->connect_keys); wdev->connect_keys = connkeys; + wdev->ibss_fixed = params->channel_fixed; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.channel = params->channel; #endif -- cgit v1.2.3 From 26ab9a0c589db9ba2710f042c4959da25fd3297b Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:00 +0200 Subject: cfg80211: introduce cfg80211_get_chan_state Helper function for finding out which channel is used by a given interface. An exclusive channel can be used only by a single interface. This is mainly for non-fixed channel IBSS handling. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/chan.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 12 ++++++++++++ 2 files changed, 63 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index c1999e45a07c..167e7cb60089 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -92,3 +92,54 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); } + +void +cfg80211_get_chan_state(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct ieee80211_channel **chan, + enum cfg80211_chan_mode *chanmode) +{ + *chan = NULL; + *chanmode = CHAN_MODE_UNDEFINED; + + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return; + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + if (wdev->current_bss) { + *chan = wdev->current_bss->pub.channel; + *chanmode = wdev->ibss_fixed + ? CHAN_MODE_SHARED + : CHAN_MODE_EXCLUSIVE; + return; + } + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (wdev->current_bss) { + *chan = wdev->current_bss->pub.channel; + *chanmode = CHAN_MODE_SHARED; + return; + } + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: + *chan = wdev->channel; + *chanmode = CHAN_MODE_SHARED; + return; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + /* these interface types don't really have a channel */ + return; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + WARN_ON(1); + } + + return; +} diff --git a/net/wireless/core.h b/net/wireless/core.h index fef476d2117e..56f18c2eb919 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -241,6 +241,12 @@ struct cfg80211_cached_keys { int def, defmgmt; }; +enum cfg80211_chan_mode { + CHAN_MODE_UNDEFINED, + CHAN_MODE_SHARED, + CHAN_MODE_EXCLUSIVE, +}; + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -419,6 +425,12 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, return cfg80211_can_change_interface(rdev, NULL, iftype); } +void +cfg80211_get_chan_state(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct ieee80211_channel **chan, + enum cfg80211_chan_mode *chanmode); + struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); -- cgit v1.2.3 From dbbae26afa81320b3315fb4ad755b20f1ff256b4 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:01 +0200 Subject: cfg80211: track monitor interfaces count Implements .set_monitor_enabled(wiphy, enabled). Notifies driver upon change of interface layout. If only monitor interfaces become present it is called with 2nd argument being true. If non-monitor interface appears then 2nd argument is false. Driver is notified only upon change. This makes it more obvious about the fact that cfg80211 supports single monitor channel. Once we implement multi-channel we don't want to allow setting monitor channel while other interface types are running. Otherwise it would be ambiguous once we start considering num_different_channels. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/wireless/core.c | 24 ++++++++++++++++++++++++ net/wireless/core.h | 14 ++++++++++++++ net/wireless/util.c | 5 +++++ 4 files changed, 47 insertions(+) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e030c6af86dd..f0d213dd8fe7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1499,6 +1499,8 @@ struct cfg80211_gtk_rekey_data { * interfaces are active this callback should reject the configuration. * If no interfaces are active or the device is down, the channel should * be stored for when a monitor interface becomes active. + * @set_monitor_enabled: Notify driver that there are only monitor + * interfaces running. * @get_channel: Get the current operating channel, should return %NULL if * there's no single defined operating channel if for example the * device implements channel hopping for multi-channel virtual interfaces. @@ -1817,6 +1819,8 @@ struct cfg80211_ops { struct ethtool_stats *stats, u64 *data); void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data); + + void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled); }; /* diff --git a/net/wireless/core.c b/net/wireless/core.c index c65f59c952c9..8412da7d0f25 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -717,6 +717,24 @@ static struct device_type wiphy_type = { .name = "wlan", }; +void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, int num) +{ + bool has_monitors_only_old = cfg80211_has_monitors_only(rdev); + bool has_monitors_only_new; + + ASSERT_RDEV_LOCK(rdev); + + rdev->num_running_ifaces += num; + if (iftype == NL80211_IFTYPE_MONITOR) + rdev->num_running_monitor_ifaces += num; + + has_monitors_only_new = cfg80211_has_monitors_only(rdev); + if (has_monitors_only_new != has_monitors_only_old) + rdev->ops->set_monitor_enabled(&rdev->wiphy, + has_monitors_only_new); +} + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ndev) @@ -820,6 +838,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; case NETDEV_DOWN: dev_hold(dev); + cfg80211_lock_rdev(rdev); + cfg80211_update_iface_num(rdev, wdev->iftype, -1); + cfg80211_unlock_rdev(rdev); queue_work(cfg80211_wq, &wdev->cleanup_work); break; case NETDEV_UP: @@ -927,6 +948,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ret = cfg80211_can_add_interface(rdev, wdev->iftype); if (ret) return notifier_from_errno(ret); + cfg80211_lock_rdev(rdev); + cfg80211_update_iface_num(rdev, wdev->iftype, 1); + cfg80211_unlock_rdev(rdev); break; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 56f18c2eb919..99acd51343b1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -56,6 +56,9 @@ struct cfg80211_registered_device { u32 ap_beacons_nlpid; + int num_running_ifaces; + int num_running_monitor_ifaces; + /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; @@ -197,6 +200,14 @@ static inline void wdev_unlock(struct wireless_dev *wdev) #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) +static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) +{ + ASSERT_RDEV_LOCK(rdev); + + return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces && + rdev->num_running_ifaces > 0; +} + enum cfg80211_event_type { EVENT_CONNECT_RESULT, EVENT_ROAMED, @@ -444,6 +455,9 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int); +void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, int num); + #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else diff --git a/net/wireless/util.c b/net/wireless/util.c index fc948d0a53f3..9b92ec57d07b 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -871,6 +871,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, } } + if (!err && ntype != otype && netif_running(dev)) { + cfg80211_update_iface_num(rdev, ntype, 1); + cfg80211_update_iface_num(rdev, otype, -1); + } + return err; } -- cgit v1.2.3 From 4f03c1ed8901a01ad4abcef95c02c007a2d481c2 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:03 +0200 Subject: cfg80211: refuse to .set_monitor_channel when non-monitors are present Having .set_monitor_channel work with non-monitor interfaces running would make interface combinations accounting ambiguous. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/chan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 167e7cb60089..019401b0b5e3 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -85,6 +85,8 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; + if (!cfg80211_has_monitors_only(rdev)) + return -EBUSY; chan = rdev_freq_to_chan(rdev, freq, chantype); if (!chan) -- cgit v1.2.3 From b78e8ceac23655e1e06b30aa95ab11742d1ac7c0 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:04 +0200 Subject: cfg80211: track monitor channel Make it even more obvious we support single monitor channel. This will allow us to remove .get_channel. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/chan.c | 9 ++++++++- net/wireless/core.c | 8 +++++++- net/wireless/core.h | 3 +++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 019401b0b5e3..434c56b92c3c 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -82,6 +82,7 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type chantype) { struct ieee80211_channel *chan; + int err; if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; @@ -92,7 +93,13 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, if (!chan) return -EINVAL; - return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); + err = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); + if (!err) { + rdev->monitor_channel = chan; + rdev->monitor_channel_type = chantype; + } + + return err; } void diff --git a/net/wireless/core.c b/net/wireless/core.c index 8412da7d0f25..1b5daa73b3a9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -730,9 +730,15 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; has_monitors_only_new = cfg80211_has_monitors_only(rdev); - if (has_monitors_only_new != has_monitors_only_old) + if (has_monitors_only_new != has_monitors_only_old) { rdev->ops->set_monitor_enabled(&rdev->wiphy, has_monitors_only_new); + + if (!has_monitors_only_new) { + rdev->monitor_channel = NULL; + rdev->monitor_channel_type = NL80211_CHAN_NO_HT; + } + } } static int cfg80211_netdev_notifier_call(struct notifier_block *nb, diff --git a/net/wireless/core.h b/net/wireless/core.h index 99acd51343b1..d5efe1b0a8f7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -59,6 +59,9 @@ struct cfg80211_registered_device { int num_running_ifaces; int num_running_monitor_ifaces; + struct ieee80211_channel *monitor_channel; + enum nl80211_channel_type monitor_channel_type; + /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; -- cgit v1.2.3 From a69b40a95b121c4bfc52a20cccd28708ae550af1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:05 +0200 Subject: cfg80211: set initial monitor channel Implements behaviour seen in mac80211. A running monitor always has a channel - even before .set_channel. This way we won't break current behaviour. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/core.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index 1b5daa73b3a9..580551e9caba 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -717,6 +717,35 @@ static struct device_type wiphy_type = { .name = "wlan", }; +static struct ieee80211_channel * +cfg80211_get_any_chan(struct cfg80211_registered_device *rdev) +{ + struct ieee80211_supported_band *sband; + int i; + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = rdev->wiphy.bands[i]; + if (sband && sband->n_channels > 0) + return &sband->channels[0]; + } + + return NULL; +} + +static void cfg80211_init_mon_chan(struct cfg80211_registered_device *rdev) +{ + struct ieee80211_channel *chan; + + chan = cfg80211_get_any_chan(rdev); + if (WARN_ON(!chan)) + return; + + mutex_lock(&rdev->devlist_mtx); + WARN_ON(cfg80211_set_monitor_channel(rdev, chan->center_freq, + NL80211_CHAN_NO_HT)); + mutex_unlock(&rdev->devlist_mtx); +} + void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num) { @@ -737,6 +766,8 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, if (!has_monitors_only_new) { rdev->monitor_channel = NULL; rdev->monitor_channel_type = NL80211_CHAN_NO_HT; + } else { + cfg80211_init_mon_chan(rdev); } } } -- cgit v1.2.3 From 2e165b818456ecc1024dd0387eeac64745526377 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:06 +0200 Subject: cfg80211/mac80211: remove .get_channel We do not need it anymore since cfg80211 tracks monitor channel and monitor channel type. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ------ net/mac80211/cfg.c | 11 ----------- net/wireless/nl80211.c | 15 +++++---------- net/wireless/wext-compat.c | 9 ++------- 4 files changed, 7 insertions(+), 34 deletions(-) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f0d213dd8fe7..fa269347355b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1501,9 +1501,6 @@ struct cfg80211_gtk_rekey_data { * be stored for when a monitor interface becomes active. * @set_monitor_enabled: Notify driver that there are only monitor * interfaces running. - * @get_channel: Get the current operating channel, should return %NULL if - * there's no single defined operating channel if for example the - * device implements channel hopping for multi-channel virtual interfaces. * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). @@ -1810,9 +1807,6 @@ struct cfg80211_ops { struct net_device *dev, u16 noack_map); - struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy, - enum nl80211_channel_type *type); - int (*get_et_sset_count)(struct wiphy *wiphy, struct net_device *dev, int sset); void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ea4b1ea9105a..ccbe2413142a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2980,16 +2980,6 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, return 0; } -static struct ieee80211_channel * -ieee80211_wiphy_get_channel(struct wiphy *wiphy, - enum nl80211_channel_type *type) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - *type = local->_oper_channel_type; - return local->oper_channel; -} - static void ieee80211_set_monitor_enabled(struct wiphy *wiphy, bool enabled) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -3073,7 +3063,6 @@ struct cfg80211_ops mac80211_config_ops = { .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, .probe_client = ieee80211_probe_client, - .get_channel = ieee80211_wiphy_get_channel, .set_noack_map = ieee80211_set_noack_map, .set_monitor_enabled = ieee80211_set_monitor_enabled, #ifdef CONFIG_PM diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 12096b4ebf62..5d29ed1f7c62 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1689,16 +1689,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, (cfg80211_rdev_list_generation << 2))) goto nla_put_failure; - if (rdev->ops->get_channel) { - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; - - chan = rdev->ops->get_channel(&rdev->wiphy, &channel_type); - if (chan && - (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - channel_type))) + if (rdev->monitor_channel) { + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + rdev->monitor_channel->center_freq) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + rdev->monitor_channel_type)) goto nla_put_failure; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index bc879833b21f..7df42f541873 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -827,8 +827,6 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -836,13 +834,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); case NL80211_IFTYPE_MONITOR: - if (!rdev->ops->get_channel) + if (!rdev->monitor_channel) return -EINVAL; - chan = rdev->ops->get_channel(wdev->wiphy, &channel_type); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; + freq->m = rdev->monitor_channel->center_freq; freq->e = 6; return 0; default: -- cgit v1.2.3 From d4e50c5917e110451ced8f8de594cea858791f37 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:07 +0200 Subject: cfg80211: add channel checking for iface combinations .connect cannot be handled since the driver scans and connects on its own. It is up to the driver then to refuse a connection (with -EBUSY for example). Non-fixed channel IBSSes always take a single channel resource. For example two non-fixed channel IBSSes always take up 2 num_different_channels, even if they operate on the same channel at a given point of time. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/core.c | 8 ++++++++ net/wireless/core.h | 29 +++++++++++++++++++++++--- net/wireless/util.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 88 insertions(+), 8 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index 580551e9caba..b26695ad3e97 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -373,6 +373,14 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) if (WARN_ON(!c->num_different_channels)) return -EINVAL; + /* + * Put a sane limit on maximum number of different + * channels to simplify channel accounting code. + */ + if (WARN_ON(c->num_different_channels > + CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) + return -EINVAL; + if (WARN_ON(!c->n_limits)) return -EINVAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index d5efe1b0a8f7..81fef3ddb5a8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -428,9 +428,20 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, u32 *flags, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype); +int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype, + struct ieee80211_channel *chan, + enum cfg80211_chan_mode chanmode); + +static inline int +cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype) +{ + return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, + CHAN_MODE_UNDEFINED); +} static inline int cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, @@ -439,6 +450,16 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, return cfg80211_can_change_interface(rdev, NULL, iftype); } +static inline int +cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + enum cfg80211_chan_mode chanmode) +{ + return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + chan, chanmode); +} + void cfg80211_get_chan_state(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, @@ -461,6 +482,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 + #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else diff --git a/net/wireless/util.c b/net/wireless/util.c index 9b92ec57d07b..4713cea9a2fa 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -938,13 +938,20 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, return res; } -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype) +int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + enum nl80211_iftype iftype, + struct ieee80211_channel *chan, + enum cfg80211_chan_mode chanmode) { struct wireless_dev *wdev_iter; u32 used_iftypes = BIT(iftype); int num[NUM_NL80211_IFTYPES]; + struct ieee80211_channel + *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS]; + struct ieee80211_channel *ch; + enum cfg80211_chan_mode chmode; + int num_different_channels = 0; int total = 1; int i, j; @@ -955,9 +962,23 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, return 0; memset(num, 0, sizeof(num)); + memset(used_channels, 0, sizeof(used_channels)); num[iftype] = 1; + switch (chanmode) { + case CHAN_MODE_UNDEFINED: + break; + case CHAN_MODE_SHARED: + WARN_ON(!chan); + used_channels[0] = chan; + num_different_channels++; + break; + case CHAN_MODE_EXCLUSIVE: + num_different_channels++; + break; + } + mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { if (wdev_iter == wdev) @@ -968,6 +989,31 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) continue; + cfg80211_get_chan_state(rdev, wdev_iter, &ch, &chmode); + + switch (chmode) { + case CHAN_MODE_UNDEFINED: + break; + case CHAN_MODE_SHARED: + for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++) + if (!used_channels[i] || used_channels[i] == ch) + break; + + if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) { + mutex_unlock(&rdev->devlist_mtx); + return -EBUSY; + } + + if (used_channels[i] == NULL) { + used_channels[i] = ch; + num_different_channels++; + } + break; + case CHAN_MODE_EXCLUSIVE: + num_different_channels++; + break; + } + num[wdev_iter->iftype]++; total++; used_iftypes |= BIT(wdev_iter->iftype); @@ -984,12 +1030,15 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, c = &rdev->wiphy.iface_combinations[i]; + if (total > c->max_interfaces) + continue; + if (num_different_channels > c->num_different_channels) + continue; + limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, GFP_KERNEL); if (!limits) return -ENOMEM; - if (total > c->max_interfaces) - goto cont; for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { if (rdev->wiphy.software_iftypes & BIT(iftype)) -- cgit v1.2.3 From e4e32459c2daea59516da59999706b357e6eb480 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:08 +0200 Subject: cfg80211: respect iface combinations when starting operation devlist_mtx locking is changed to accomodate changes. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/wireless/core.c | 2 ++ net/wireless/ibss.c | 10 ++++++++++ net/wireless/mesh.c | 12 ++++++++++++ net/wireless/mlme.c | 16 ++++++++++++++++ net/wireless/nl80211.c | 8 ++++++++ net/wireless/util.c | 9 ++++----- 6 files changed, 52 insertions(+), 5 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index b26695ad3e97..ca2b95f24846 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -990,7 +990,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); + mutex_lock(&rdev->devlist_mtx); ret = cfg80211_can_add_interface(rdev, wdev->iftype); + mutex_unlock(&rdev->devlist_mtx); if (ret) return notifier_from_errno(ret); cfg80211_lock_rdev(rdev); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index b90fd86b2d18..ca5672f6ee2f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -118,6 +118,16 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->wext.ibss.channel = params->channel; #endif wdev->sme_state = CFG80211_SME_CONNECTING; + + err = cfg80211_can_use_chan(rdev, wdev, params->channel, + params->channel_fixed + ? CHAN_MODE_SHARED + : CHAN_MODE_EXCLUSIVE); + if (err) { + wdev->connect_keys = NULL; + return err; + } + err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); if (err) { wdev->connect_keys = NULL; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index bab381344723..c384e77ff77a 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -155,6 +155,11 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, setup->channel_type)) return -EINVAL; + err = cfg80211_can_use_chan(rdev, wdev, setup->channel, + CHAN_MODE_SHARED); + if (err) + return err; + err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); @@ -173,9 +178,11 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_mesh(rdev, dev, setup, conf); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -208,6 +215,11 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return -ENETDOWN; + err = cfg80211_can_use_chan(rdev, wdev, channel, + CHAN_MODE_SHARED); + if (err) + return err; + err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, wdev->netdev, channel); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index a7882eb8c46e..d4fece3bb18a 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -302,8 +302,14 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, if (!req.bss) return -ENOENT; + err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, + CHAN_MODE_SHARED); + if (err) + goto out; + err = rdev->ops->auth(&rdev->wiphy, dev, &req); +out: cfg80211_put_bss(req.bss); return err; } @@ -317,11 +323,13 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, { int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key, key_len, key_idx); wdev_unlock(dev->ieee80211_ptr); + mutex_unlock(&rdev->devlist_mtx); return err; } @@ -397,8 +405,14 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, return -ENOENT; } + err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, + CHAN_MODE_SHARED); + if (err) + goto out; + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); +out: if (err) { if (was_connected) wdev->sme_state = CFG80211_SME_CONNECTED; @@ -421,11 +435,13 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, ssid, ssid_len, ie, ie_len, use_mfp, crypt, assoc_flags, ht_capa, ht_capa_mask); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5d29ed1f7c62..77102e66f1ea 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2478,6 +2478,14 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.channel_type)) return -EINVAL; + mutex_lock(&rdev->devlist_mtx); + err = cfg80211_can_use_chan(rdev, wdev, params.channel, + CHAN_MODE_SHARED); + mutex_unlock(&rdev->devlist_mtx); + + if (err) + return err; + err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); if (!err) { wdev->preset_chan = params.channel; diff --git a/net/wireless/util.c b/net/wireless/util.c index 4713cea9a2fa..a9260ac85cf1 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -805,8 +805,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return -EBUSY; if (ntype != otype && netif_running(dev)) { + mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, ntype); + mutex_unlock(&rdev->devlist_mtx); if (err) return err; @@ -956,6 +958,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, int i, j; ASSERT_RTNL(); + lockdep_assert_held(&rdev->devlist_mtx); /* Always allow software iftypes */ if (rdev->wiphy.software_iftypes & BIT(iftype)) @@ -979,7 +982,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, break; } - mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { if (wdev_iter == wdev) continue; @@ -999,10 +1001,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, if (!used_channels[i] || used_channels[i] == ch) break; - if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) { - mutex_unlock(&rdev->devlist_mtx); + if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) return -EBUSY; - } if (used_channels[i] == NULL) { used_channels[i] = ch; @@ -1018,7 +1018,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, total++; used_iftypes |= BIT(wdev_iter->iftype); } - mutex_unlock(&rdev->devlist_mtx); if (total == 1) return 0; -- cgit v1.2.3 From 3a0c52a6d82cc41da965284412608c74aece34e4 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 2 Jul 2012 09:32:32 +0300 Subject: cfg80211: add 802.11ad (60gHz band) support Add enumerations for both cfg80211 and nl80211. This expands wiphy.bands etc. arrays. Extend channel <-> frequency translation to cover 60g band and modify the rate check logic since there are no legacy mandatory rates (only MCS is used.) Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/3945-rs.c | 2 +- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 2 ++ net/mac80211/tx.c | 2 ++ net/wireless/core.c | 10 ++++++++-- net/wireless/util.c | 35 +++++++++++++++++++++++++-------- 6 files changed, 42 insertions(+), 11 deletions(-) (limited to 'net/wireless') diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 4b10157d8686..d4fd29ad90dc 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -946,7 +946,7 @@ il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) case IEEE80211_BAND_5GHZ: rs_sta->expected_tpt = il3945_expected_tpt_a; break; - case IEEE80211_NUM_BANDS: + default: BUG(); break; } diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 23003272c70e..74cc55c1bf28 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2545,10 +2545,12 @@ enum nl80211_tx_rate_attributes { * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, }; /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fa269347355b..0b564e83a24b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -70,11 +70,13 @@ * * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) + * @IEEE80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) * @IEEE80211_NUM_BANDS: number of defined bands */ enum ieee80211_band { IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, + IEEE80211_BAND_60GHZ = NL80211_BAND_60GHZ, /* keep last */ IEEE80211_NUM_BANDS diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4e753032e48d..4990f4fb5864 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -140,6 +140,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (r->flags & IEEE80211_RATE_MANDATORY_A) mrate = r->bitrate; break; + case IEEE80211_BAND_60GHZ: + /* TODO, for now fall through */ case IEEE80211_NUM_BANDS: WARN_ON(1); break; diff --git a/net/wireless/core.c b/net/wireless/core.c index ca2b95f24846..e13365f1fa63 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -468,8 +468,14 @@ int wiphy_register(struct wiphy *wiphy) continue; sband->band = band; - - if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) + if (WARN_ON(!sband->n_channels)) + return -EINVAL; + /* + * on 60gHz band, there are no legacy rates, so + * n_bitrates is 0 + */ + if (WARN_ON(band != IEEE80211_BAND_60GHZ && + !sband->n_bitrates)) return -EINVAL; /* diff --git a/net/wireless/util.c b/net/wireless/util.c index a9260ac85cf1..0228c64e73d8 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -35,19 +35,29 @@ int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) { /* see 802.11 17.3.8.3.2 and Annex J * there are overlapping channel numbers in 5GHz and 2GHz bands */ - if (band == IEEE80211_BAND_5GHZ) { - if (chan >= 182 && chan <= 196) - return 4000 + chan * 5; - else - return 5000 + chan * 5; - } else { /* IEEE80211_BAND_2GHZ */ + if (chan <= 0) + return 0; /* not supported */ + switch (band) { + case IEEE80211_BAND_2GHZ: if (chan == 14) return 2484; else if (chan < 14) return 2407 + chan * 5; + break; + case IEEE80211_BAND_5GHZ: + if (chan >= 182 && chan <= 196) + return 4000 + chan * 5; else - return 0; /* not supported */ + return 5000 + chan * 5; + break; + case IEEE80211_BAND_60GHZ: + if (chan < 5) + return 56160 + chan * 2160; + break; + default: + ; } + return 0; /* not supported */ } EXPORT_SYMBOL(ieee80211_channel_to_frequency); @@ -60,8 +70,12 @@ int ieee80211_frequency_to_channel(int freq) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; - else + else if (freq <= 45000) /* DMG band lower limit */ return (freq - 5000) / 5; + else if (freq >= 58320 && freq <= 64800) + return (freq - 56160) / 2160; + else + return 0; } EXPORT_SYMBOL(ieee80211_frequency_to_channel); @@ -137,6 +151,11 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, } WARN_ON(want != 0 && want != 3 && want != 6); break; + case IEEE80211_BAND_60GHZ: + /* check for mandatory HT MCS 1..4 */ + WARN_ON(!sband->ht_cap.ht_supported); + WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e); + break; case IEEE80211_NUM_BANDS: WARN_ON(1); break; -- cgit v1.2.3 From 90cdc6df71c3716e660f7d99926d5f24b461c6cc Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 2 Jul 2012 09:32:34 +0300 Subject: wireless: regulatory for 60g Add regulatory rule for the 60g band Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- net/wireless/reg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index baf5704740ee..b2b32229b607 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -129,7 +129,7 @@ static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { - .n_reg_rules = 5, + .n_reg_rules = 6, .alpha2 = "00", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ @@ -156,6 +156,9 @@ static const struct ieee80211_regdomain world_regdom = { REG_RULE(5745-10, 5825+10, 40, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + + /* IEEE 802.11ad (60gHz), channels 1..3 */ + REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), } }; -- cgit v1.2.3 From c5a7e582490c423f0685e42ee5cfb7c6de81adb0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jul 2012 13:28:18 +0200 Subject: cfg80211: fix locking regression in monitor channel tracking Michal's monitor channel tracking introduce a locking problem as it locked the rdev lock inside the netdev notifier which isn't allowed as we might already hold it if we get there by removing an interface that is up. Fix this by relying only on the RTNL to protect the interface counters, the RTNL is always held in these code paths anyway. Signed-off-by: Johannes Berg --- net/wireless/core.c | 8 ++------ net/wireless/core.h | 4 +++- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index e13365f1fa63..eb60410ae588 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -766,7 +766,7 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, bool has_monitors_only_old = cfg80211_has_monitors_only(rdev); bool has_monitors_only_new; - ASSERT_RDEV_LOCK(rdev); + ASSERT_RTNL(); rdev->num_running_ifaces += num; if (iftype == NL80211_IFTYPE_MONITOR) @@ -888,10 +888,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, wdev->beacon_interval = 0; break; case NETDEV_DOWN: - dev_hold(dev); - cfg80211_lock_rdev(rdev); cfg80211_update_iface_num(rdev, wdev->iftype, -1); - cfg80211_unlock_rdev(rdev); + dev_hold(dev); queue_work(cfg80211_wq, &wdev->cleanup_work); break; case NETDEV_UP: @@ -1001,9 +999,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, mutex_unlock(&rdev->devlist_mtx); if (ret) return notifier_from_errno(ret); - cfg80211_lock_rdev(rdev); cfg80211_update_iface_num(rdev, wdev->iftype, 1); - cfg80211_unlock_rdev(rdev); break; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 81fef3ddb5a8..377dc394f48c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "reg.h" @@ -56,6 +57,7 @@ struct cfg80211_registered_device { u32 ap_beacons_nlpid; + /* protected by RTNL only */ int num_running_ifaces; int num_running_monitor_ifaces; @@ -205,7 +207,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev) static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) { - ASSERT_RDEV_LOCK(rdev); + ASSERT_RTNL(); return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces && rdev->num_running_ifaces > 0; -- cgit v1.2.3 From 8eb41c8dfb9e2396d2452ada9023a83d610b9051 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 5 Jul 2012 14:25:49 +0300 Subject: {nl,cfg}80211: support high bitrates Until now, a u16 value was used to represent bitrate value. With VHT bitrates this becomes too small. Introduce a new 32-bit bitrate attribute. nl80211 will report both the new and the old attribute, unless the bitrate doesn't fit into the old u16 attribute in which case only the new one will be reported. User space tools encouraged to prefer the 32-bit attribute, if available (since it won't be available on older kernels.) Signed-off-by: Vladimir Kondratiev [reword commit message and comments a bit] Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 9 +++++++++ include/net/cfg80211.h | 2 +- net/wireless/nl80211.c | 9 +++++++-- net/wireless/util.c | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'net/wireless') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 74cc55c1bf28..db961a59247f 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1638,12 +1638,20 @@ struct nl80211_sta_flag_update { * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ @@ -1653,6 +1661,7 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0b564e83a24b..8837efc368f9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3487,7 +3487,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, * * return 0 if MCS index >= 32 */ -u16 cfg80211_calculate_bitrate(struct rate_info *rate); +u32 cfg80211_calculate_bitrate(struct rate_info *rate); /* Logging, debugging and troubleshooting/diagnostic helpers. */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 77102e66f1ea..2a5cdb60bc6e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2618,7 +2618,8 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) { struct nlattr *rate; - u16 bitrate; + u32 bitrate; + u16 bitrate_compat; rate = nla_nest_start(msg, attr); if (!rate) @@ -2626,8 +2627,12 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ bitrate = cfg80211_calculate_bitrate(info); + /* report 16-bit bitrate only if we can */ + bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; if ((bitrate > 0 && - nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate)) || + nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) || + (bitrate_compat > 0 && + nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) || ((info->flags & RATE_INFO_FLAGS_MCS) && nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) || ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) && diff --git a/net/wireless/util.c b/net/wireless/util.c index 0228c64e73d8..6e52726f7fe3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -900,7 +900,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } -u16 cfg80211_calculate_bitrate(struct rate_info *rate) +u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; -- cgit v1.2.3 From 95ddc1fc4519ed48ddc7d622bd5c84dff3eebb0a Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 5 Jul 2012 14:25:50 +0300 Subject: cfg80211: bitrate calculation for 60g 60g band uses different from .11n MCS scheme, so bitrate should be calculated differently Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/util.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'net/wireless') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8837efc368f9..51f67a9003a9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -580,11 +580,13 @@ enum station_info_flags { * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval + * @RATE_INFO_FLAGS_60G: 60gHz MCS */ enum rate_info_flags { RATE_INFO_FLAGS_MCS = 1<<0, RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, RATE_INFO_FLAGS_SHORT_GI = 1<<2, + RATE_INFO_FLAGS_60G = 1<<3, }; /** diff --git a/net/wireless/util.c b/net/wireless/util.c index 6e52726f7fe3..e31f1dba79ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -900,12 +900,61 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } +static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) +{ + static const u32 __mcs2bitrate[] = { + /* control PHY */ + [0] = 275, + /* SC PHY */ + [1] = 3850, + [2] = 7700, + [3] = 9625, + [4] = 11550, + [5] = 12512, /* 1251.25 mbps */ + [6] = 15400, + [7] = 19250, + [8] = 23100, + [9] = 25025, + [10] = 30800, + [11] = 38500, + [12] = 46200, + /* OFDM PHY */ + [13] = 6930, + [14] = 8662, /* 866.25 mbps */ + [15] = 13860, + [16] = 17325, + [17] = 20790, + [18] = 27720, + [19] = 34650, + [20] = 41580, + [21] = 45045, + [22] = 51975, + [23] = 62370, + [24] = 67568, /* 6756.75 mbps */ + /* LP-SC PHY */ + [25] = 6260, + [26] = 8340, + [27] = 11120, + [28] = 12510, + [29] = 16680, + [30] = 22240, + [31] = 25030, + }; + + if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate))) + return 0; + + return __mcs2bitrate[rate->mcs]; +} + u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; if (!(rate->flags & RATE_INFO_FLAGS_MCS)) return rate->legacy; + if (rate->flags & RATE_INFO_FLAGS_60G) + return cfg80211_calculate_bitrate_60g(rate); /* the formula below does only work for MCS values smaller than 32 */ if (WARN_ON_ONCE(rate->mcs >= 32)) -- cgit v1.2.3