From 42d97a599eb6b2aab3a401b3e5799a399d6c7652 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 18:31:02 +0100 Subject: cfg80211: remove remain-on-channel channel type As mwifiex (and mac80211 in the software case) are the only drivers actually implementing remain-on-channel with channel type, userspace can't be relying on it. This is the case, as it's used only for P2P operations right now. Rather than adding a flag to tell userspace whether or not it can actually rely on it, simplify all the code by removing the ability to use different channel types. Leave only the validation of the attribute, so that if we extend it again later (with the needed capability flag), it can't break userspace sending invalid data. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1a9a819cfab0..43cd6fa084c5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -401,8 +401,7 @@ * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the - * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be - * optionally used to specify additional channel parameters. + * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the @@ -440,12 +439,11 @@ * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the - * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and - * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. If this - * channel is not the current channel (remain-on-channel or the - * operational channel) the device will switch to the given channel and - * transmit the frame, optionally waiting for a response for the time + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. -- cgit v1.2.3 From fe4b31810c06cc6518fb193efb9b3c3289b55832 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 19:20:56 +0100 Subject: nl80211: add documentation for channel type Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 43cd6fa084c5..82b5ad38435b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2438,6 +2438,15 @@ enum nl80211_ac { #define NL80211_TXQ_Q_BE NL80211_AC_BE #define NL80211_TXQ_Q_BK NL80211_AC_BK +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, -- cgit v1.2.3 From 3d9d1d6656a73ea8407734cfb00b81d14ef62d4b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Nov 2012 23:14:50 +0100 Subject: 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 --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 +- include/net/cfg80211.h | 73 ++++++++- include/uapi/linux/nl80211.h | 61 +++++-- net/mac80211/cfg.c | 5 +- net/mac80211/ibss.c | 13 +- net/wireless/chan.c | 249 ++++++++++++++++++++++++++--- net/wireless/core.h | 6 + net/wireless/ibss.c | 4 +- net/wireless/mesh.c | 4 +- net/wireless/nl80211.c | 160 ++++++++++++------ net/wireless/trace.h | 28 ++-- net/wireless/wext-compat.c | 4 +- net/wireless/wext-sme.c | 3 +- 13 files changed, 507 insertions(+), 113 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c0cc2e59fe6c..51bbe85c574c 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1099,12 +1099,10 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, "channel switch notify nw_type %d freq %d mode %d\n", vif->nw_type, freq, mode); - chandef.chan = ieee80211_get_channel(vif->ar->wiphy, freq); - if (WARN_ON(!chandef.chan)) - return; - - chandef._type = (mode == WMI_11G_HT20) ? - NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + cfg80211_chandef_create(&chandef, + ieee80211_get_channel(vif->ar->wiphy, freq), + (mode == WMI_11G_HT20) ? + NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); cfg80211_ch_switch_notify(vif->ndev, &chandef); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 86f777af79e8..977da58fb7ea 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -308,20 +308,85 @@ struct key_params { /** * struct cfg80211_chan_def - channel definition * @chan: the (control) channel - * @_type: the channel type, don't use this field, - * use cfg80211_get_chandef_type() if needed. + * @width: channel width + * @center_freq1: center frequency of first segment + * @center_freq2: center frequency of second segment + * (only with 80+80 MHz) */ struct cfg80211_chan_def { struct ieee80211_channel *chan; - enum nl80211_channel_type _type; + enum nl80211_chan_width width; + u32 center_freq1; + u32 center_freq2; }; +/** + * cfg80211_get_chandef_type - return old channel type from chandef + * @chandef: the channel definition + * + * Returns the old channel type (NOHT, HT20, HT40+/-) from a given + * chandef, which must have a bandwidth allowing this conversion. + */ static inline enum nl80211_channel_type cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef) { - return chandef->_type; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return NL80211_CHAN_NO_HT; + case NL80211_CHAN_WIDTH_20: + return NL80211_CHAN_HT20; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + return NL80211_CHAN_HT40PLUS; + return NL80211_CHAN_HT40MINUS; + default: + WARN_ON(1); + return NL80211_CHAN_NO_HT; + } +} + +/** + * cfg80211_chandef_create - create channel definition using channel type + * @chandef: the channel definition struct to fill + * @channel: the control channel + * @chantype: the channel type + * + * Given a channel type, create a channel definition. + */ +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *channel, + enum nl80211_channel_type chantype); + +/** + * cfg80211_chandef_identical - check if two channel definitions are identical + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %true if the channels defined by the channel definitions are + * identical, %false otherwise. + */ +static inline bool +cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2) +{ + return (chandef1->chan == chandef2->chan && + chandef1->width == chandef2->width && + chandef1->center_freq1 == chandef2->center_freq1 && + chandef1->center_freq2 == chandef2->center_freq2); } +/** + * cfg80211_chandef_compatible - check if two channel definitions are compatible + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %NULL if the given channel definitions are incompatible, + * chandef1 or chandef2 otherwise. + */ +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2); + /** * enum survey_info_flags - survey information flags * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 82b5ad38435b..84f9c7d84c69 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -118,8 +118,9 @@ * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, - * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL @@ -171,7 +172,7 @@ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * The channel to use can be set on the interface or be given using the - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -471,8 +472,8 @@ * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ - * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed - * by %NL80211_ATTR_IFINDEX) shall operate on. + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -566,8 +567,8 @@ * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating - * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with @@ -771,14 +772,26 @@ enum nl80211_commands { * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters - * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ - * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 @@ -1553,6 +1566,10 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_FLAGS, + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2454,6 +2471,32 @@ enum nl80211_channel_type { NL80211_CHAN_HT40PLUS }; +/** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, +}; + /** * enum nl80211_bss - netlink attributes for a BSS * 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); -- cgit v1.2.3 From db9c64cf8d9d3fcbc34b09d037f266d1fc9f928c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 9 Nov 2012 14:56:41 +0100 Subject: nl80211/cfg80211: add VHT MCS support Add support for reporting and calculating VHT MCSes. Note that I'm not completely sure that the bitrate calculations are correct, nor that they can't be simplified. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 24 +++++++++----- include/uapi/linux/nl80211.h | 12 ++++++- net/wireless/nl80211.c | 58 +++++++++++++++++++++++++--------- net/wireless/util.c | 74 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 24 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 977da58fb7ea..e78db2cf3d1b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -662,16 +662,24 @@ enum station_info_flags { * Used by the driver to indicate the specific rate transmission * type for 802.11n transmissions. * - * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled - * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission + * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS + * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS + * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 MHz width transmission + * @RATE_INFO_FLAGS_80_MHZ_WIDTH: 80 MHz width transmission + * @RATE_INFO_FLAGS_80P80_MHZ_WIDTH: 80+80 MHz width transmission + * @RATE_INFO_FLAGS_160_MHZ_WIDTH: 160 MHz width transmission * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval - * @RATE_INFO_FLAGS_60G: 60gHz MCS + * @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, + RATE_INFO_FLAGS_MCS = BIT(0), + RATE_INFO_FLAGS_VHT_MCS = BIT(1), + RATE_INFO_FLAGS_40_MHZ_WIDTH = BIT(2), + RATE_INFO_FLAGS_80_MHZ_WIDTH = BIT(3), + RATE_INFO_FLAGS_80P80_MHZ_WIDTH = BIT(4), + RATE_INFO_FLAGS_160_MHZ_WIDTH = BIT(5), + RATE_INFO_FLAGS_SHORT_GI = BIT(6), + RATE_INFO_FLAGS_60G = BIT(7), }; /** @@ -682,11 +690,13 @@ enum rate_info_flags { * @flags: bitflag of flags from &enum rate_info_flags * @mcs: mcs index if struct describes a 802.11n bitrate * @legacy: bitrate in 100kbit/s for 802.11abg + * @nss: number of streams (VHT only) */ struct rate_info { u8 flags; u8 mcs; u16 legacy; + u8 nss; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 84f9c7d84c69..33a417481ad8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1734,10 +1734,15 @@ struct nl80211_sta_flag_update { * @__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_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_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -1747,6 +1752,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 15158a3d64a3..d038fa45ecd1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2890,29 +2890,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, rate = nla_nest_start(msg, attr); if (!rate) - goto nla_put_failure; + return false; /* 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_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) && - nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) || - ((info->flags & RATE_INFO_FLAGS_SHORT_GI) && - nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))) - goto nla_put_failure; + if (bitrate > 0 && + nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) + return false; + if (bitrate_compat > 0 && + nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) + return false; + + if (info->flags & RATE_INFO_FLAGS_MCS) { + if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) + return false; + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_SHORT_GI && + nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) + return false; + } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { + if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs)) + return false; + if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss)) + return false; + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_SHORT_GI && + nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) + return false; + } nla_nest_end(msg, rate); return true; - -nla_put_failure: - return false; } static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, @@ -5475,6 +5498,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) return -EINVAL; + if (ibss.chandef.width > NL80211_CHAN_WIDTH_40) + return -EINVAL; + if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && + !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) + ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; diff --git a/net/wireless/util.c b/net/wireless/util.c index db61fe8a6b6d..3cce6e486219 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -944,14 +944,86 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) return __mcs2bitrate[rate->mcs]; } +static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate) +{ + static const u32 base[4][10] = { + { 6500000, + 13000000, + 19500000, + 26000000, + 39000000, + 52000000, + 58500000, + 65000000, + 78000000, + 0, + }, + { 13500000, + 27000000, + 40500000, + 54000000, + 81000000, + 108000000, + 121500000, + 135000000, + 162000000, + 180000000, + }, + { 29300000, + 58500000, + 87800000, + 117000000, + 175500000, + 234000000, + 263300000, + 292500000, + 351000000, + 390000000, + }, + { 58500000, + 117000000, + 175500000, + 234000000, + 351000000, + 468000000, + 526500000, + 585000000, + 702000000, + 780000000, + }, + }; + u32 bitrate; + int idx; + + if (WARN_ON_ONCE(rate->mcs > 9)) + return 0; + + idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH | + RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 : + rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 : + rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0; + + bitrate = base[idx][rate->mcs]; + bitrate *= rate->nss; + + if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) + bitrate = (bitrate / 9) * 10; + + /* do NOT round down here */ + return (bitrate + 50000) / 100000; +} + u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; - if (!(rate->flags & RATE_INFO_FLAGS_MCS)) + if (!(rate->flags & RATE_INFO_FLAGS_MCS) && + !(rate->flags & RATE_INFO_FLAGS_VHT_MCS)) return rate->legacy; if (rate->flags & RATE_INFO_FLAGS_60G) return cfg80211_calculate_bitrate_60g(rate); + if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) + return cfg80211_calculate_bitrate_vht(rate); /* the formula below does only work for MCS values smaller than 32 */ if (WARN_ON_ONCE(rate->mcs >= 32)) -- cgit v1.2.3