* [PATCH 1/3] cfg80211: rework chandef checking and export it
2012-11-26 14:25 [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
@ 2012-11-26 14:25 ` Johannes Berg
2012-11-26 14:25 ` [PATCH 2/3] mac80211: support VHT association Johannes Berg
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2012-11-26 14:25 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Some of the chandef checking that we do in cfg80211
to check if a channel is supported or not is also
needed in mac80211, so rework that a bit and export
the functions that are needed.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/cfg80211.h | 21 ++++++++++--
net/wireless/chan.c | 92 ++++++++++++++++++++++++++++++++++----------------
net/wireless/core.h | 6 ----
net/wireless/nl80211.c | 37 ++------------------
4 files changed, 84 insertions(+), 72 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index e78db2c..a238f41 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -58,6 +58,8 @@
* structures here describe these capabilities in detail.
*/
+struct wiphy;
+
/*
* wireless hardware capability structures
*/
@@ -388,6 +390,22 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
const struct cfg80211_chan_def *chandef2);
/**
+ * cfg80211_chandef_valid - check if a channel definition is valid
+ * @chandef: the channel definition to check
+ */
+bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
+
+/**
+ * cfg80211_chandef_usable - check if secondary channels can be used
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * @prohibited_flags: the regulatory chanenl flags that must not be set
+ */
+bool cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ u32 prohibited_flags);
+
+/**
* enum survey_info_flags - survey information flags
*
* @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -1045,9 +1063,6 @@ struct ieee80211_txq_params {
u8 aifs;
};
-/* from net/wireless.h */
-struct wiphy;
-
/**
* DOC: Scanning and BSS list handling
*
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index bf2dfd5..b5f6983 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -44,7 +44,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
}
EXPORT_SYMBOL(cfg80211_chandef_create);
-bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
+bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{
u32 control_freq;
@@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
return true;
}
+EXPORT_SYMBOL(cfg80211_chandef_valid);
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
int *pri40, int *pri80)
@@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
}
EXPORT_SYMBOL(cfg80211_chandef_compatible);
-bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
- u32 center_freq, u32 bandwidth,
- u32 prohibited_flags)
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
{
struct ieee80211_channel *c;
u32 freq;
@@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
return true;
}
-static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
- u32 center_freq, u32 bw)
+bool cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ u32 prohibited_flags)
{
- return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
- IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR);
-}
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u32 width, control_freq;
-bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef)
-{
- u32 width;
- bool res;
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
- trace_cfg80211_reg_can_beacon(wiphy, chandef);
+ ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
+ vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
- if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
- trace_cfg80211_return_bool(false);
- return false;
- }
+ control_freq = chandef->chan->center_freq;
switch (chandef->width) {
- case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
+ if (!ht_cap->ht_supported)
+ return false;
+ case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
break;
case NL80211_CHAN_WIDTH_40:
width = 40;
+ if (!ht_cap->ht_supported)
+ return false;
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
+ return false;
+ if (chandef->center_freq1 < control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ return false;
+ if (chandef->center_freq1 > control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ return false;
break;
- case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_80P80:
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ return false;
+ case NL80211_CHAN_WIDTH_80:
+ if (!vht_cap->vht_supported)
+ return false;
width = 80;
break;
case NL80211_CHAN_WIDTH_160:
+ if (!vht_cap->vht_supported)
+ return false;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
+ return false;
width = 160;
break;
default:
WARN_ON_ONCE(1);
- trace_cfg80211_return_bool(false);
return false;
}
- res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
+ /* TODO: missing regulatory check on 80/160 bandwidth */
+
+ if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
+ width, prohibited_flags))
+ return false;
+
+ if (!chandef->center_freq2)
+ return true;
+ return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
+ width, prohibited_flags);
+}
+EXPORT_SYMBOL(cfg80211_chandef_usable);
+
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ bool res;
+
+ trace_cfg80211_reg_can_beacon(wiphy, chandef);
- if (res && chandef->center_freq2)
- res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
- width);
+ res = cfg80211_chandef_usable(wiphy, chandef,
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_RADAR);
trace_cfg80211_return_bool(res);
return res;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a0c8dec..6183a0d 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -483,12 +483,6 @@ 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/nl80211.c b/net/wireless/nl80211.c
index d038fa4..ced04f2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1420,63 +1420,32 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
- if (!cfg80211_chan_def_valid(chandef))
+ if (!cfg80211_chandef_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_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;
- if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
- ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
- return -EINVAL;
break;
case NL80211_CHAN_WIDTH_80:
width = 80;
- if (!vht_cap->vht_supported)
- return -EINVAL;
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))
+ if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, 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;
}
@@ -1841,7 +1810,7 @@ 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));
+ WARN_ON(!cfg80211_chandef_valid(chandef));
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq))
--
1.8.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 2/3] mac80211: support VHT association
2012-11-26 14:25 [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
2012-11-26 14:25 ` [PATCH 1/3] cfg80211: rework chandef checking and export it Johannes Berg
@ 2012-11-26 14:25 ` Johannes Berg
2012-11-27 12:12 ` Mahesh Palivela
2012-11-26 14:25 ` [PATCH 3/3] mac80211: support (partial) VHT radiotap information Johannes Berg
2012-11-28 12:24 ` [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
3 siblings, 1 reply; 7+ messages in thread
From: Johannes Berg @ 2012-11-26 14:25 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Determine the VHT channel from the AP's VHT operation IE
(if present) and configure the hardware to that channel
if it is supported. If channel contexts cause a channel
to not be usable, try a smaller bandwidth.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/linux/ieee80211.h | 15 ++
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/mlme.c | 370 ++++++++++++++++++++++++++++++++++++---------
3 files changed, 315 insertions(+), 72 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index f9c5a78..8f690e5 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1213,6 +1213,21 @@ struct ieee80211_vht_cap {
} __packed;
/**
+ * enum ieee80211_vht_chanwidth - VHT channel width
+ * @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to
+ * determine the channel width (20 or 40 MHz)
+ * @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth
+ * @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth
+ * @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth
+ */
+enum ieee80211_vht_chanwidth {
+ IEEE80211_VHT_CHANWIDTH_USE_HT = 0,
+ IEEE80211_VHT_CHANWIDTH_80MHZ = 1,
+ IEEE80211_VHT_CHANWIDTH_160MHZ = 2,
+ IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3,
+};
+
+/**
* struct ieee80211_vht_operation - VHT operation IE
*
* This structure is the "VHT operation element" as
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0a8f83d..8d8bf71 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -371,6 +371,8 @@ enum ieee80211_sta_flags {
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
IEEE80211_STA_DISABLE_40MHZ = BIT(10),
IEEE80211_STA_DISABLE_VHT = BIT(11),
+ IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
+ IEEE80211_STA_DISABLE_160MHZ = BIT(13),
};
struct ieee80211_mgd_auth_data {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d2a4f78..e15977f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
/* determine capability flags */
cap = vht_cap.cap;
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
/* reserve and fill IE */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
+ if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
sband, chan, sdata->smps_mode);
@@ -3183,23 +3197,272 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
return 0;
}
+static u32 chandef_downgrade(struct cfg80211_chan_def *c)
+{
+ u32 ret;
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_20:
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ c->width = NL80211_CHAN_WIDTH_20;
+ c->center_freq1 = c->chan->center_freq;
+ ret = IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
+ c->width = NL80211_CHAN_WIDTH_40;
+ ret = IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ c->center_freq2 = 0;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P80 */
+ tmp /= 4;
+ c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ default:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ WARN_ON_ONCE(1);
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ }
+
+ WARN_ON_ONCE(!cfg80211_chandef_valid(c));
+
+ return ret;
+}
+
+static u32
+ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_channel *channel,
+ const struct ieee80211_ht_operation *ht_oper,
+ const struct ieee80211_vht_operation *vht_oper,
+ struct cfg80211_chan_def *chandef)
+{
+ struct cfg80211_chan_def vht_chandef;
+ u32 ht_cfreq, ret;
+
+ chandef->chan = channel;
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = channel->center_freq;
+ chandef->center_freq2 = 0;
+
+ if (!ht_oper || !sband->ht_cap.ht_supported) {
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ chandef->width = NL80211_CHAN_WIDTH_20;
+
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ channel->band);
+ /* check that channel matches the right operating channel */
+ if (channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ sdata_info(sdata,
+ "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
+ channel->center_freq, ht_cfreq,
+ ht_oper->primary_chan, channel->band);
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ /* check 40 MHz support, if we have it */
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 += 10;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 -= 10;
+ break;
+ }
+ } else {
+ /* 40 MHz (and 80 MHz) must be supported for VHT */
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!vht_oper || !sband->vht_cap.vht_supported) {
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ vht_chandef.chan = channel;
+ vht_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
+ channel->band);
+ vht_chandef.center_freq2 = 0;
+
+ if (vht_oper->center_freq_seg2_idx)
+ vht_chandef.center_freq2 =
+ ieee80211_channel_to_frequency(
+ vht_oper->center_freq_seg2_idx,
+ channel->band);
+
+ switch (vht_oper->chan_width) {
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
+ vht_chandef.width = chandef->width;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_160;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+ break;
+ default:
+ sdata_info(sdata,
+ "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
+ vht_oper->chan_width);
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_valid(&vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information is invalid, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information doesn't match HT, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ *chandef = vht_chandef;
+
+ ret = 0;
+
+ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED)) {
+ if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
+ ret = IEEE80211_STA_DISABLE_HT |
+ IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ ret = chandef_downgrade(chandef);
+ }
+
+ if (chandef->width != vht_chandef.width)
+ sdata_info(sdata,
+ "local regulatory prevented using AP HT/VHT configuration, downgraded\n");
+
+out:
+ WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
+ return ret;
+}
+
+static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_bss *cbss)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ht_cap_ie, *vht_cap_ie;
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct ieee80211_vht_cap *vht_cap;
+ u8 chains = 1;
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
+ return chains;
+
+ ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
+ ht_cap = (void *)(ht_cap_ie + 2);
+ chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
+ /*
+ * TODO: use "Tx Maximum Number Spatial Streams Supported" and
+ * "Tx Unequal Modulation Supported" fields.
+ */
+
+ }
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+ return chains;
+
+
+ vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
+ u8 nss;
+ u16 tx_mcs_map;
+
+ vht_cap = (void *)(vht_cap_ie + 2);
+ tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+ for (nss = 8; nss > 0; nss--) {
+ if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
+ IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ break;
+ }
+ /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
+ chains = max(chains, nss);
+ }
+
+ return chains;
+}
+
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- int ht_cfreq;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL;
+ const struct ieee80211_vht_operation *vht_oper = NULL;
struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef;
+ int ret;
sband = local->hw.wiphy->bands[cbss->channel->band];
- ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+ ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ);
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ sband->ht_cap.ht_supported) {
+ const u8 *ht_oper_ie;
- if (sband->ht_cap.ht_supported) {
ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
cbss->information_elements,
cbss->len_information_elements);
@@ -3207,82 +3470,45 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
ht_oper = (void *)(ht_oper_ie + 2);
}
- if (ht_oper) {
- ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
- cbss->channel->band);
- /* check that channel matches the right operating channel */
- if (cbss->channel->center_freq != ht_cfreq) {
- /*
- * It's possible that some APs are confused here;
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel in association responses, but
- * since we look at probe response/beacon data here
- * it should be OK.
- */
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ sband->vht_cap.vht_supported) {
+ const u8 *vht_oper_ie;
+
+ vht_oper_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
+ vht_oper = (void *)(vht_oper_ie + 2);
+ if (vht_oper && !ht_oper) {
+ vht_oper = NULL;
sdata_info(sdata,
- "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
- cbss->channel->center_freq,
- ht_cfreq, ht_oper->primary_chan,
- cbss->channel->band);
- ht_oper = NULL;
+ "AP advertised VHT without HT, disabling both\n");
+ sdata->flags |= IEEE80211_STA_DISABLE_HT;
+ sdata->flags |= IEEE80211_STA_DISABLE_VHT;
}
}
- if (ht_oper) {
- /*
- * cfg80211 already verified that the channel itself can
- * be used, but it didn't check that we can do the right
- * HT type, so do that here as well. If HT40 isn't allowed
- * on this channel, disable 40 MHz operation.
- */
- const u8 *ht_cap_ie;
- const struct ieee80211_ht_cap *ht_cap;
- u8 chains = 1;
-
- channel_type = NL80211_CHAN_HT20;
-
- if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
- switch (ht_oper->ht_param &
- IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (cbss->channel->flags &
- IEEE80211_CHAN_NO_HT40PLUS)
- ifmgd->flags |=
- IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (cbss->channel->flags &
- IEEE80211_CHAN_NO_HT40MINUS)
- ifmgd->flags |=
- IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
+ cbss->channel,
+ ht_oper, vht_oper,
+ &chandef);
- ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
- cbss->information_elements,
- cbss->len_information_elements);
- if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
- ht_cap = (void *)(ht_cap_ie + 2);
- chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
- }
- sdata->needed_rx_chains = min(chains, local->rx_chains);
- } else {
- sdata->needed_rx_chains = 1;
- sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT;
- }
+ sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
+ local->rx_chains);
/* will change later if needed */
sdata->smps_mode = IEEE80211_SMPS_OFF;
- ieee80211_vif_release_channel(sdata);
- cfg80211_chandef_create(&chandef, cbss->channel, channel_type);
- return ieee80211_vif_use_channel(sdata, &chandef,
- IEEE80211_CHANCTX_SHARED);
+ /*
+ * If this fails (possibly due to channel context sharing
+ * on incompatible channels, e.g. 80+80 and 160 sharing the
+ * same control channel) try to use a smaller bandwidth.
+ */
+ ret = ieee80211_vif_use_channel(sdata, &chandef,
+ IEEE80211_CHANCTX_SHARED);
+ while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
+ ifmgd->flags |= chandef_downgrade(&chandef);
+ return ret;
}
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
--
1.8.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 2/3] mac80211: support VHT association
2012-11-26 14:25 ` [PATCH 2/3] mac80211: support VHT association Johannes Berg
@ 2012-11-27 12:12 ` Mahesh Palivela
2012-11-27 12:44 ` Johannes Berg
0 siblings, 1 reply; 7+ messages in thread
From: Mahesh Palivela @ 2012-11-27 12:12 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Johannes Berg
On 11/26/2012 07:55 PM, Johannes Berg wrote:
> +static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
> + struct cfg80211_bss *cbss)
> +{
> + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
> + const u8 *ht_cap_ie, *vht_cap_ie;
> + const struct ieee80211_ht_cap *ht_cap;
> + const struct ieee80211_vht_cap *vht_cap;
> + u8 chains = 1;
> +
> + if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
> + return chains;
> +
> + ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
> + cbss->information_elements,
> + cbss->len_information_elements);
> + if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
> + ht_cap = (void *)(ht_cap_ie + 2);
> + chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
> + /*
> + * TODO: use "Tx Maximum Number Spatial Streams Supported" and
> + * "Tx Unequal Modulation Supported" fields.
> + */
> +
> + }
> +
> + if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
> + return chains;
> +
> +
> + vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY,
> + cbss->information_elements,
> + cbss->len_information_elements);
> + if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
> + u8 nss;
> + u16 tx_mcs_map;
> +
> + vht_cap = (void *)(vht_cap_ie + 2);
> + tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
Guess we should take rx_mcs_map as we are determining vht_rx_chains?
Thanks,
Mahesh
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH 2/3] mac80211: support VHT association
2012-11-27 12:12 ` Mahesh Palivela
@ 2012-11-27 12:44 ` Johannes Berg
0 siblings, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2012-11-27 12:44 UTC (permalink / raw)
To: Mahesh Palivela; +Cc: linux-wireless
On Tue, 2012-11-27 at 17:42 +0530, Mahesh Palivela wrote:
> > + vht_cap = (void *)(vht_cap_ie + 2);
> > + tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
>
> Guess we should take rx_mcs_map as we are determining vht_rx_chains?
No, when we determine our needed RX chains, we need to know what the AP
can transmit to us, not what the AP can receive.
johannes
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/3] mac80211: support (partial) VHT radiotap information
2012-11-26 14:25 [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
2012-11-26 14:25 ` [PATCH 1/3] cfg80211: rework chandef checking and export it Johannes Berg
2012-11-26 14:25 ` [PATCH 2/3] mac80211: support VHT association Johannes Berg
@ 2012-11-26 14:25 ` Johannes Berg
2012-11-28 12:24 ` [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
3 siblings, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2012-11-26 14:25 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Add some information that we have about VHT to radiotap.
This at least lets one see the MCS and NSS information.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/ieee80211_radiotap.h | 24 ++++++++++++++++++++++++
include/net/mac80211.h | 5 +++++
net/mac80211/main.c | 2 ++
net/mac80211/rx.c | 40 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 71 insertions(+)
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index 7f0df13..c399963 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -186,6 +186,10 @@ struct ieee80211_radiotap_header {
* IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless
*
* Contains the AMPDU information for the subframe.
+ *
+ * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16
+ *
+ * Contains VHT information about this frame.
*/
enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_TSFT = 0,
@@ -209,6 +213,7 @@ enum ieee80211_radiotap_type {
IEEE80211_RADIOTAP_MCS = 19,
IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+ IEEE80211_RADIOTAP_VHT = 21,
/* valid in every it_present bitmap, even vendor namespaces */
IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
@@ -282,6 +287,25 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020
+/* For IEEE80211_RADIOTAP_VHT */
+#define IEEE80211_RADIOTAP_VHT_KNOWN_STBC 0x0001
+#define IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA 0x0002
+#define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004
+#define IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS 0x0008
+#define IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010
+#define IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED 0x0020
+#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040
+#define IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID 0x0080
+#define IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID 0x0100
+
+#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01
+#define IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA 0x02
+#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04
+#define IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 0x08
+#define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10
+#define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20
+
+
/* helpers */
static inline int ieee80211_get_radiotap_len(unsigned char *data)
{
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index db7680a..e806f1d 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1473,6 +1473,10 @@ enum ieee80211_hw_flags {
* include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only
* adding _BW is supported today.
*
+ * @radiotap_vht_details: lists which VHT MCS information the HW reports,
+ * the default is _GI | _BANDWIDTH.
+ * Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
+ *
* @netdev_features: netdev features to be set in each netdev created
* from this HW. Note only HW checksum features are currently
* compatible with mac80211. Other feature bits will be rejected.
@@ -1499,6 +1503,7 @@ struct ieee80211_hw {
u8 max_tx_aggregation_subframes;
u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details;
+ u16 radiotap_vht_details;
netdev_features_t netdev_features;
};
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6e93340..c4ed83b 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -638,6 +638,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW;
+ local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 825f33c..9b13b8b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 8;
}
+ if (status->flag & RX_FLAG_VHT) {
+ len = ALIGN(len, 2);
+ len += 12;
+ }
+
if (status->vendor_radiotap_len) {
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
status->vendor_radiotap_align = 1;
@@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0;
}
+ if (status->flag & RX_FLAG_VHT) {
+ u16 known = local->hw.radiotap_vht_details;
+
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+ /* known field - how to handle 80+80? */
+ if (status->flag & RX_FLAG_80P80MHZ)
+ known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ put_unaligned_le16(known, pos);
+ pos += 2;
+ /* flags */
+ if (status->flag & RX_FLAG_SHORT_GI)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ pos++;
+ /* bandwidth */
+ if (status->flag & RX_FLAG_80MHZ)
+ *pos++ = 4;
+ else if (status->flag & RX_FLAG_80P80MHZ)
+ *pos++ = 0; /* marked not known above */
+ else if (status->flag & RX_FLAG_160MHZ)
+ *pos++ = 11;
+ else if (status->flag & RX_FLAG_40MHZ)
+ *pos++ = 1;
+ else /* 20 MHz */
+ *pos++ = 0;
+ /* MCS/NSS */
+ *pos = (status->rate_idx << 4) | status->vht_nss;
+ pos += 4;
+ /* coding field */
+ pos++;
+ /* group ID */
+ pos++;
+ /* partial_aid */
+ pos += 2;
+ }
+
if (status->vendor_radiotap_len) {
/* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1)
--
1.8.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 0/3] mac80211: VHT managed mode & radiotap support
2012-11-26 14:25 [PATCH 0/3] mac80211: VHT managed mode & radiotap support Johannes Berg
` (2 preceding siblings ...)
2012-11-26 14:25 ` [PATCH 3/3] mac80211: support (partial) VHT radiotap information Johannes Berg
@ 2012-11-28 12:24 ` Johannes Berg
3 siblings, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2012-11-28 12:24 UTC (permalink / raw)
To: linux-wireless
On Mon, 2012-11-26 at 15:25 +0100, Johannes Berg wrote:
> For managed mode, we have to parse the VHT operation IE to
> determine what the channel setting should be, and then take
> into account local capabilities for the channel to use,
> possibly disabling some capabilities due to regulatory.
>
> Also add some radiotap support so we differentiate VHT from
> other (HT or legacy) frames.
Applied, with a few whitespace changes.
johannes
^ permalink raw reply [flat|nested] 7+ messages in thread