summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-11-08 23:14:50 +0100
committerJohannes Berg <johannes.berg@intel.com>2012-11-26 12:42:59 +0100
commit3d9d1d6656a73ea8407734cfb00b81d14ef62d4b (patch)
tree74f8b3b4acd0b91068ebebbfaa00aaef1f7e097e /net
parentcfg80211: pass a channel definition struct (diff)
downloadlinux-3d9d1d6656a73ea8407734cfb00b81d14ef62d4b.tar.xz
linux-3d9d1d6656a73ea8407734cfb00b81d14ef62d4b.zip
nl80211/cfg80211: support VHT channel configuration
Change nl80211 to support specifying a VHT (or HT) using the control channel frequency (as before) and new attributes for the channel width and first and second center frequency. The old channel type is of course still supported for HT. Also change the cfg80211 channel definition struct to support these by adding the relevant fields to it (and removing the _type field.) This also adds new helper functions: - cfg80211_chandef_create to create a channel def struct given the control channel and channel type, - cfg80211_chandef_identical to check if two channel definitions are identical - cfg80211_chandef_compatible to check if the given channel definitions are compatible, and return the wider of the two This isn't entirely complete, but that doesn't matter until we have a driver using it. In particular, it's missing - regulatory checks on the usable bandwidth (if that even makes sense) - regulatory TX power (database can't deal with it) - a proper channel compatibility calculation for the new channel types Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c5
-rw-r--r--net/mac80211/ibss.c13
-rw-r--r--net/wireless/chan.c249
-rw-r--r--net/wireless/core.h6
-rw-r--r--net/wireless/ibss.c4
-rw-r--r--net/wireless/mesh.c4
-rw-r--r--net/wireless/nl80211.c160
-rw-r--r--net/wireless/trace.h28
-rw-r--r--net/wireless/wext-compat.c4
-rw-r--r--net/wireless/wext-sme.c3
10 files changed, 382 insertions, 94 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index fbb2d072cb9e..7136b945798e 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3125,8 +3125,9 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
- chandef->chan = chanctx_conf->channel;
- chandef->_type = chanctx_conf->channel_type;
+ cfg80211_chandef_create(chandef,
+ chanctx_conf->channel,
+ chanctx_conf->channel_type);
ret = 0;
}
rcu_read_unlock();
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index bed616fd97e9..5648bbed240b 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -52,6 +52,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
struct cfg80211_chan_def chandef;
+ enum nl80211_channel_type chan_type;
lockdep_assert_held(&ifibss->mtx);
@@ -79,13 +80,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- chandef.chan = chan;
- chandef._type = ifibss->channel_type;
+ chan_type = ifibss->channel_type;
+ cfg80211_chandef_create(&chandef, chan, chan_type);
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef))
- chandef._type = NL80211_CHAN_HT20;
+ chan_type = NL80211_CHAN_HT20;
ieee80211_vif_release_channel(sdata);
- if (ieee80211_vif_use_channel(sdata, chan, chandef._type,
+ if (ieee80211_vif_use_channel(sdata, chan, chan_type,
ifibss->fixed_channel ?
IEEE80211_CHANCTX_SHARED :
IEEE80211_CHANCTX_EXCLUSIVE)) {
@@ -159,7 +160,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ifibss->ie, ifibss->ie_len);
/* add HT capability and information IEs */
- if (chandef._type != NL80211_CHAN_NO_HT &&
+ if (chan_type != NL80211_CHAN_NO_HT &&
sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
@@ -172,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- chan, chandef._type, 0);
+ chan, chan_type, 0);
}
if (local->hw.queues >= IEEE80211_NUM_ACS) {
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index e834422de40a..bf2dfd54ff3b 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -11,43 +11,252 @@
#include "core.h"
#include "rdev-ops.h"
-bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef)
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
{
- struct ieee80211_channel *sec_chan;
- int diff;
+ if (WARN_ON(!chan))
+ return;
- trace_cfg80211_reg_can_beacon(wiphy, chandef);
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
- switch (chandef->_type) {
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
case NL80211_CHAN_HT40PLUS:
- diff = 20;
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
break;
case NL80211_CHAN_HT40MINUS:
- diff = -20;
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL(cfg80211_chandef_create);
+
+bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
+{
+ u32 control_freq;
+
+ if (!chandef->chan)
+ return false;
+
+ control_freq = chandef->chan->center_freq;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ if (chandef->center_freq1 != control_freq)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (!chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (chandef->center_freq1 != control_freq + 70 &&
+ chandef->center_freq1 != control_freq + 50 &&
+ chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30 &&
+ chandef->center_freq1 != control_freq - 50 &&
+ chandef->center_freq1 != control_freq - 70)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
+ int *pri40, int *pri80)
+{
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_40:
+ *pri40 = c->center_freq1;
+ *pri80 = 0;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ *pri80 = c->center_freq1;
+ /* n_P20 */
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 20 + 40 * tmp;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 60 + 40 * tmp;
+ /* n_P80 */
+ tmp /= 2;
+ *pri80 = c->center_freq1 - 40 + 80 * tmp;
break;
default:
- trace_cfg80211_return_bool(true);
- return true;
+ WARN_ON_ONCE(1);
}
+}
+
+const struct cfg80211_chan_def *
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
+ const struct cfg80211_chan_def *c2)
+{
+ u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
- sec_chan = ieee80211_get_channel(wiphy,
- chandef->chan->center_freq + diff);
- if (!sec_chan) {
+ /* If they are identical, return */
+ if (cfg80211_chandef_identical(c1, c2))
+ return c1;
+
+ /* otherwise, must have same control channel */
+ if (c1->chan != c2->chan)
+ return NULL;
+
+ /*
+ * If they have the same width, but aren't identical,
+ * then they can't be compatible.
+ */
+ if (c1->width == c2->width)
+ return NULL;
+
+ if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c1->width == NL80211_CHAN_WIDTH_20)
+ return c2;
+
+ if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c2->width == NL80211_CHAN_WIDTH_20)
+ return c1;
+
+ chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
+ chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
+
+ if (c1_pri40 != c2_pri40)
+ return NULL;
+
+ WARN_ON(!c1_pri80 && !c2_pri80);
+ if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
+ return NULL;
+
+ if (c1->width > c2->width)
+ return c1;
+ return c2;
+}
+EXPORT_SYMBOL(cfg80211_chandef_compatible);
+
+bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
+ return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
+ u32 center_freq, u32 bw)
+{
+ return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_RADAR);
+}
+
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ u32 width;
+ bool res;
+
+ trace_cfg80211_reg_can_beacon(wiphy, chandef);
+
+ if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
trace_cfg80211_return_bool(false);
return false;
}
- /* we'll need a DFS capability later */
- if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR)) {
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
trace_cfg80211_return_bool(false);
return false;
}
- trace_cfg80211_return_bool(true);
- return true;
+
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
+
+ if (res && chandef->center_freq2)
+ res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
+ width);
+
+ trace_cfg80211_return_bool(res);
+ return res;
}
EXPORT_SYMBOL(cfg80211_reg_can_beacon);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 6183a0d25b8b..a0c8decf6a47 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -483,6 +483,12 @@ 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);
+bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef);
+
+bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags);
+
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index ccc8865dfadb..9b9551e4a6f9 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -252,7 +252,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
/* try to find an IBSS channel if none requested ... */
if (!wdev->wext.ibss.chandef.chan) {
- wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT;
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband;
@@ -352,7 +352,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
if (chan) {
wdev->wext.ibss.chandef.chan = chan;
- wdev->wext.ibss.chandef._type = NL80211_CHAN_NO_HT;
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
wdev->wext.ibss.channel_fixed = true;
} else {
/* cfg80211_ibss_wext_join will pick one if needed */
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 12b5a570a306..3ee5a7282283 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -146,7 +146,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!setup->chandef.chan)
return -EINVAL;
- setup->chandef._type = NL80211_CHAN_NO_HT;
+ setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;;
}
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
@@ -198,7 +198,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
* compatible with 802.11 mesh.
*/
if (rdev->ops->libertas_set_mesh_channel) {
- if (chandef->_type != NL80211_CHAN_NO_HT)
+ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
return -EINVAL;
if (!netif_running(wdev->netdev))
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 999108cd947c..15158a3d64a3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -1360,35 +1365,13 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_P2P_GO;
}
-static bool nl80211_valid_channel_type(struct genl_info *info,
- enum nl80211_channel_type *channel_type)
-{
- enum nl80211_channel_type tmp;
-
- if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
- return false;
-
- tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
- if (tmp != NL80211_CHAN_NO_HT &&
- tmp != NL80211_CHAN_HT20 &&
- tmp != NL80211_CHAN_HT40PLUS &&
- tmp != NL80211_CHAN_HT40MINUS)
- return false;
-
- if (channel_type)
- *channel_type = tmp;
-
- return true;
-}
-
static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct genl_info *info,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_sta_ht_cap *ht_cap;
- struct ieee80211_channel *sc;
- u32 control_freq;
- int offs;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u32 control_freq, width;
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
return -EINVAL;
@@ -1396,47 +1379,105 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
- chandef->_type = NL80211_CHAN_NO_HT;
-
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &chandef->_type))
- return -EINVAL;
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = control_freq;
+ chandef->center_freq2 = 0;
/* Primary channel not allowed */
if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type chantype;
+
+ chantype = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (chantype) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40PLUS:
+ case NL80211_CHAN_HT40MINUS:
+ cfg80211_chandef_create(chandef, chandef->chan,
+ chantype);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+ chandef->width =
+ nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+ chandef->center_freq1 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+ chandef->center_freq2 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
+ vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
- switch (chandef->_type) {
- case NL80211_CHAN_NO_HT:
+ if (!cfg80211_chan_def_valid(chandef))
+ return -EINVAL;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ if (!ht_cap->ht_supported)
+ return -EINVAL;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ width = 20;
break;
- case NL80211_CHAN_HT40MINUS:
- if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ /* quick early regulatory check */
+ if (chandef->center_freq1 < control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ return -EINVAL;
+ if (chandef->center_freq1 > control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ return -EINVAL;
+ if (!ht_cap->ht_supported)
return -EINVAL;
- offs = -20;
- /* fall through */
- case NL80211_CHAN_HT40PLUS:
- if (chandef->_type == NL80211_CHAN_HT40PLUS) {
- if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
- return -EINVAL;
- offs = 20;
- }
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
return -EINVAL;
-
- sc = ieee80211_get_channel(&rdev->wiphy,
- chandef->chan->center_freq + offs);
- if (!sc || sc->flags & IEEE80211_CHAN_DISABLED)
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ width = 80;
+ if (!vht_cap->vht_supported)
return -EINVAL;
- /* fall through */
- case NL80211_CHAN_HT20:
- if (!ht_cap->ht_supported)
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ width = 80;
+ if (!vht_cap->vht_supported)
+ return -EINVAL;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ return -EINVAL;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ if (!vht_cap->vht_supported)
+ return -EINVAL;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
return -EINVAL;
break;
+ default:
+ return -EINVAL;
}
+ if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1,
+ width, IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+ if (chandef->center_freq2 &&
+ !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2,
+ width, IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ /* TODO: missing regulatory check on bandwidth */
+
return 0;
}
@@ -1800,10 +1841,28 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
static int nl80211_send_chandef(struct sk_buff *msg,
struct cfg80211_chan_def *chandef)
{
+ WARN_ON(!cfg80211_chan_def_valid(chandef));
+
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq))
return -ENOBUFS;
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, chandef->_type))
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ cfg80211_get_chandef_type(chandef)))
+ return -ENOBUFS;
+ break;
+ default:
+ break;
+ }
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+ return -ENOBUFS;
+ if (chandef->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
return -ENOBUFS;
return 0;
}
@@ -5447,7 +5506,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
- if ((ibss.chandef._type != NL80211_CHAN_NO_HT) && no_ht) {
+ if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+ no_ht) {
kfree(connkeys);
return -EINVAL;
}
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 1370d52b1393..3c7aa1221563 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -126,25 +126,33 @@
#define CHAN_PR_FMT ", band: %d, freq: %u"
#define CHAN_PR_ARG __entry->band, __entry->center_freq
-#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \
- __field(u16, center_freq) \
- __field(u32, channel_type)
+#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \
+ __field(u32, control_freq) \
+ __field(u32, width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2)
#define CHAN_DEF_ASSIGN(chandef) \
do { \
if ((chandef) && (chandef)->chan) { \
__entry->band = (chandef)->chan->band; \
- __entry->center_freq = \
+ __entry->control_freq = \
(chandef)->chan->center_freq; \
- __entry->channel_type = (chandef)->_type; \
+ __entry->width = (chandef)->width; \
+ __entry->center_freq1 = (chandef)->center_freq1;\
+ __entry->center_freq2 = (chandef)->center_freq2;\
} else { \
__entry->band = 0; \
- __entry->center_freq = 0; \
- __entry->channel_type = 0; \
+ __entry->control_freq = 0; \
+ __entry->width = 0; \
+ __entry->center_freq1 = 0; \
+ __entry->center_freq2 = 0; \
} \
} while (0)
-#define CHAN_DEF_PR_FMT ", band: %d, freq: %u, chantype: %d"
-#define CHAN_DEF_PR_ARG __entry->band, __entry->center_freq, \
- __entry->channel_type
+#define CHAN_DEF_PR_FMT \
+ ", band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u"
+#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
+ __entry->width, __entry->center_freq1, \
+ __entry->center_freq2
#define SINFO_ENTRY __field(int, generation) \
__field(u32, connected_time) \
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index da3307f32362..f9680c9cf9b3 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -785,7 +785,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct cfg80211_chan_def chandef = {
- ._type = NL80211_CHAN_NO_HT,
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
};
int freq, err;
@@ -800,6 +800,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
@@ -813,6 +814,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index e6e5dbf2f616..873af63187c0 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -120,7 +120,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
*/
if (chan && !wdev->wext.connect.ssid_len) {
struct cfg80211_chan_def chandef = {
- ._type = NL80211_CHAN_NO_HT,
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ .center_freq1 = freq,
};
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);