This is the initial 802.11ah (S1G) patchset which adds support for:
- defining the S1G 900MHz bands in a custom regulatory database
- setting and receiving S1G beacons (sending short beacons will be
supported in a future patch)
- configuring S1G capabilities in Association Request (setting
capabilities along with NL80211_CMD_SET_STATION will be added later).
- scanning on S1G bands
- handling S1G Association Response format
- correctly encoding Listen Interval for S1G
- associating in mac80211
- S1G in mac80211_hwsim
Rate control is still TBD, this patchset simply lops off the rate
control hooks for S1G so eg. missing sband->bitrates and S1G Basic Rate
set can't do too much damage.
Note the mac80211_hwsim S1G support introduces a regression in a few
hostap hwsim tests. This is because when processing the reported bands,
hostap assumes freq < 4000 is 11b, and the actual 11b/g band is
overwritten by the S1G band info. Though it does count as a userspace
regression, I'm not sure there is much to do about it besides apply a
small patch to hostapd which treats freq < 2000 as an unknown band.
After the hostap workaround (https://p.ibsgaard.io/raw/xaweyacunu),
these patches continue to pass the hwsim tests as well as HEAD.
Thomas Pedersen (22):
nl80211: advertise supported channel width in S1G
cfg80211: regulatory: pass min. bandwidth to regulatory rule extractor
cfg80211: regulatory: handle S1G channels
nl80211: correctly validate S1G beacon head
nl80211: support setting S1G channels
{cfg,mac}80211: get correct default channel width for S1G
mac80211: s1g: choose scanning width based on frequency
nl80211: support S1G capabilities
mac80211: support S1G STA capabilities
cfg80211: convert S1G beacon to scan results
cfg80211: parse S1G Operation element for BSS channel
mac80211: convert S1G beacon to scan results
cfg80211: handle Association Response from S1G STA
mac80211: encode listen interval for S1G
mac80211: don't calculate duration for S1G
mac80211: handle S1G low rates
mac80211: avoid rate init for S1G band
mac80211: receive and process S1G beacons
mac80211: support S1G association
nl80211: include frequency offset in survey info
mac80211_hwsim: indicate support for S1G
mac80211_hwsim: fix TSF timestamp write to S1G beacon
drivers/net/wireless/ath/regd.c | 2 +-
.../broadcom/brcm80211/brcmsmac/channel.c | 3 +-
drivers/net/wireless/mac80211_hwsim.c | 93 +++++++-
drivers/net/wireless/realtek/rtlwifi/regd.c | 7 +-
include/linux/ieee80211.h | 82 ++++++-
include/net/cfg80211.h | 31 ++-
include/net/mac80211.h | 3 +
include/uapi/linux/nl80211.h | 26 +++
net/mac80211/cfg.c | 2 +
net/mac80211/chan.c | 9 +-
net/mac80211/ibss.c | 3 +-
net/mac80211/ieee80211_i.h | 20 ++
net/mac80211/iface.c | 5 +
net/mac80211/mlme.c | 184 +++++++++++++---
net/mac80211/rate.c | 39 +++-
net/mac80211/rx.c | 87 ++++----
net/mac80211/scan.c | 37 +++-
net/mac80211/tx.c | 4 +
net/mac80211/util.c | 207 +++++++++++++++++-
net/wireless/chan.c | 140 +++++++-----
net/wireless/mlme.c | 20 ++
net/wireless/nl80211.c | 56 ++++-
net/wireless/reg.c | 74 +++++--
net/wireless/scan.c | 80 +++++--
net/wireless/util.c | 32 +++
25 files changed, 1046 insertions(+), 200 deletions(-)
--
2.20.1
The regulatory code assumed 20Mhz is the minimum channel
bandwidth supported. Make this a parameter so S1G code can
pass smaller bandwidths.
Signed-off-by: Thomas Pedersen <[email protected]>
---
drivers/net/wireless/ath/regd.c | 2 +-
drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c | 3 ++-
drivers/net/wireless/realtek/rtlwifi/regd.c | 7 ++++---
include/net/cfg80211.h | 3 ++-
net/mac80211/util.c | 2 +-
net/wireless/nl80211.c | 3 ++-
net/wireless/reg.c | 7 ++++---
7 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index bee9110b91f3..f9c9ac16d2da 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -268,7 +268,7 @@ static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
{
const struct ieee80211_reg_rule *reg_rule;
- reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));
+ reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq), 20);
if (IS_ERR(reg_rule))
return;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index 5a6d9c86552a..d85b2707e2fa 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -677,7 +677,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
rule = freq_reg_info(wiphy,
- MHZ_TO_KHZ(ch->center_freq));
+ MHZ_TO_KHZ(ch->center_freq),
+ 20);
if (IS_ERR(rule))
continue;
diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c
index 8be31e0ad878..3f9dd4941bb1 100644
--- a/drivers/net/wireless/realtek/rtlwifi/regd.c
+++ b/drivers/net/wireless/realtek/rtlwifi/regd.c
@@ -151,7 +151,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
reg_rule = freq_reg_info(wiphy,
- ch->center_freq);
+ ch->center_freq,
+ 20);
if (IS_ERR(reg_rule))
continue;
/*
@@ -213,7 +214,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
*/
ch = &sband->channels[11]; /* CH 12 */
- reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ reg_rule = freq_reg_info(wiphy, ch->center_freq, 20);
if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
@@ -221,7 +222,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
}
ch = &sband->channels[12]; /* CH 13 */
- reg_rule = freq_reg_info(wiphy, ch->center_freq);
+ reg_rule = freq_reg_info(wiphy, ch->center_freq, 20);
if (!IS_ERR(reg_rule)) {
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 3db0444086e2..2c737d6be9a1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5862,6 +5862,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
* freq_reg_info - get regulatory information for the given frequency
* @wiphy: the wiphy for which we want to process this rule for
* @center_freq: Frequency in KHz for which we want regulatory information for
+ * @min_bw: Minimum channel bandwidth in MHz for which we want regulatory information for
*
* Use this function to get the regulatory rule for a specific frequency on
* a given wireless device. If the device has a specific regulatory domain
@@ -5877,7 +5878,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
* purely subjective and right now it's 802.11 specific.
*/
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
- u32 center_freq);
+ u32 center_freq, u32 min_bw);
/**
* reg_initiator_name - map regulatory request initiator enum to name
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index c8504ffc71a1..9ba1afe4ba1f 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1453,7 +1453,7 @@ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
return;
}
- rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));
+ rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq), 20);
if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
rcu_read_unlock();
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5aded5de35cd..dd0f62e942ed 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1013,7 +1013,8 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
if (large) {
const struct ieee80211_reg_rule *rule =
- freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+ freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
+ 20);
if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
if (nl80211_msg_put_wmm_rules(msg, rule))
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 35b8847a2f6d..6c130cec22c3 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1629,9 +1629,10 @@ __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
}
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
- u32 center_freq)
+ u32 center_freq,
+ u32 min_bw)
{
- return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
+ return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(min_bw));
}
EXPORT_SYMBOL(freq_reg_info);
@@ -1711,7 +1712,7 @@ static void handle_channel(struct wiphy *wiphy,
flags = chan->orig_flags;
- reg_rule = freq_reg_info(wiphy, ieee80211_channel_to_khz(chan));
+ reg_rule = freq_reg_info(wiphy, ieee80211_channel_to_khz(chan), 20);
if (IS_ERR(reg_rule)) {
/*
* We will disable all channels that do not match our
--
2.20.1
S1G channels have a single width defined per frequency, so
derive it from the channel flags with
ieee80211_s1g_channel_width().
Also support setting an S1G channel where control frequency may
differ from operating, and add some basic validation to
ensure the control channel is with the operating.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/net/cfg80211.h | 10 ++++
net/wireless/chan.c | 130 ++++++++++++++++++++++++-----------------
net/wireless/util.c | 27 +++++++++
3 files changed, 115 insertions(+), 52 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2c737d6be9a1..f44839823d1e 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5290,6 +5290,16 @@ ieee80211_channel_to_khz(const struct ieee80211_channel *chan)
return MHZ_TO_KHZ(chan->center_freq) + chan->freq_offset;
}
+/**
+ * ieee80211_s1g_channel_width - get allowed channel width from @chan
+ *
+ * Only allowed for band NL80211_BAND_S1GHZ
+ * @chan: channel
+ * Return: The allowed channel width for this center_freq
+ */
+enum nl80211_chan_width
+ieee80211_s1g_channel_width(const struct ieee80211_channel *chan);
+
/**
* ieee80211_channel_to_freq_khz - convert channel number to frequency
* @chan: channel number
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 90f0f82cd9ca..0a4b8d3eaed9 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -140,9 +140,62 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
return true;
}
+static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
+{
+ int mhz;
+
+ switch (chan_width) {
+ case NL80211_CHAN_WIDTH_1:
+ mhz = 1;
+ break;
+ case NL80211_CHAN_WIDTH_2:
+ mhz = 2;
+ break;
+ case NL80211_CHAN_WIDTH_4:
+ mhz = 4;
+ break;
+ case NL80211_CHAN_WIDTH_8:
+ mhz = 8;
+ break;
+ case NL80211_CHAN_WIDTH_16:
+ mhz = 16;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ mhz = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ mhz = 10;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ mhz = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ mhz = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ mhz = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ mhz = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+ return mhz;
+}
+
+static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
+{
+ return nl80211_chan_width_to_mhz(c->width);
+}
+
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{
- u32 control_freq;
+ u32 control_freq, oper_freq;
+ int oper_width, control_width;
if (!chandef->chan)
return false;
@@ -154,10 +207,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
switch (chandef->width) {
case NL80211_CHAN_WIDTH_1:
- case NL80211_CHAN_WIDTH_2:
- case NL80211_CHAN_WIDTH_4:
- case NL80211_CHAN_WIDTH_8:
- case NL80211_CHAN_WIDTH_16:
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20:
@@ -168,6 +217,30 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
if (chandef->center_freq2)
return false;
break;
+ case NL80211_CHAN_WIDTH_2:
+ case NL80211_CHAN_WIDTH_4:
+ case NL80211_CHAN_WIDTH_8:
+ case NL80211_CHAN_WIDTH_16:
+ control_freq = ieee80211_channel_to_khz(chandef->chan);
+ oper_freq = ieee80211_chandef_to_khz(chandef);
+ control_width = nl80211_chan_width_to_mhz(
+ ieee80211_s1g_channel_width(
+ chandef->chan));
+ oper_width = cfg80211_chandef_get_width(chandef);
+
+ if (oper_width < 0 || control_width < 0)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+
+ if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
+ oper_freq + MHZ_TO_KHZ(oper_width) / 2)
+ return false;
+
+ if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
+ oper_freq - MHZ_TO_KHZ(oper_width) / 2)
+ return false;
+ break;
case NL80211_CHAN_WIDTH_40:
if (chandef->center_freq1 != control_freq + 10 &&
chandef->center_freq1 != control_freq - 10)
@@ -263,53 +336,6 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
}
}
-static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
-{
- int width;
-
- switch (c->width) {
- case NL80211_CHAN_WIDTH_1:
- width = 1;
- break;
- case NL80211_CHAN_WIDTH_2:
- width = 2;
- break;
- case NL80211_CHAN_WIDTH_4:
- width = 4;
- break;
- case NL80211_CHAN_WIDTH_8:
- width = 8;
- break;
- case NL80211_CHAN_WIDTH_16:
- width = 16;
- break;
- case NL80211_CHAN_WIDTH_5:
- width = 5;
- break;
- case NL80211_CHAN_WIDTH_10:
- width = 10;
- break;
- case NL80211_CHAN_WIDTH_20:
- case NL80211_CHAN_WIDTH_20_NOHT:
- width = 20;
- break;
- case NL80211_CHAN_WIDTH_40:
- width = 40;
- break;
- case NL80211_CHAN_WIDTH_80P80:
- case NL80211_CHAN_WIDTH_80:
- width = 80;
- break;
- case NL80211_CHAN_WIDTH_160:
- width = 160;
- break;
- default:
- WARN_ON_ONCE(1);
- return -1;
- }
- return width;
-}
-
const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
const struct cfg80211_chan_def *c2)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 383278c05a75..91ffb5132501 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -111,6 +111,33 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
}
EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);
+enum nl80211_chan_width
+ieee80211_s1g_channel_width(const struct ieee80211_channel *chan)
+{
+ if (WARN_ON(!chan || chan->band != NL80211_BAND_S1GHZ))
+ return NL80211_CHAN_WIDTH_20_NOHT;
+
+ /*S1G defines a single allowed channel width per channel.
+ * Extract that width here.
+ */
+ if (chan->flags & IEEE80211_CHAN_1MHZ)
+ return NL80211_CHAN_WIDTH_1;
+ else if (chan->flags & IEEE80211_CHAN_2MHZ)
+ return NL80211_CHAN_WIDTH_2;
+ else if (chan->flags & IEEE80211_CHAN_4MHZ)
+ return NL80211_CHAN_WIDTH_4;
+ else if (chan->flags & IEEE80211_CHAN_8MHZ)
+ return NL80211_CHAN_WIDTH_8;
+ else if (chan->flags & IEEE80211_CHAN_16MHZ)
+ return NL80211_CHAN_WIDTH_16;
+
+ pr_err("unknown channel width for channel at %dKHz?\n",
+ ieee80211_channel_to_khz(chan));
+
+ return NL80211_CHAN_WIDTH_1;
+}
+EXPORT_SYMBOL(ieee80211_s1g_channel_width);
+
int ieee80211_freq_khz_to_channel(u32 freq)
{
/* TODO: just handle MHz for now */
--
2.20.1
The S1G beacon has a different header size than regular
beacons, so adjust the beacon head validator.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/wireless/nl80211.c | 16 +++++++++++++---
net/wireless/util.c | 5 +++++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a57872708108..c5950d154d66 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -209,14 +209,24 @@ static int validate_beacon_head(const struct nlattr *attr,
unsigned int len = nla_len(attr);
const struct element *elem;
const struct ieee80211_mgmt *mgmt = (void *)data;
- unsigned int fixedlen = offsetof(struct ieee80211_mgmt,
- u.beacon.variable);
+ bool s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control);
+ unsigned int fixedlen, hdrlen;
+
+ if (s1g_bcn) {
+ fixedlen = offsetof(struct ieee80211_ext,
+ u.s1g_beacon.variable);
+ hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon);
+ } else {
+ fixedlen = offsetof(struct ieee80211_mgmt,
+ u.beacon.variable);
+ hdrlen = offsetof(struct ieee80211_mgmt, u.beacon);
+ }
if (len < fixedlen)
goto err;
if (ieee80211_hdrlen(mgmt->frame_control) !=
- offsetof(struct ieee80211_mgmt, u.beacon))
+ hdrlen)
goto err;
data += fixedlen;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index dfad1c0f57ad..383278c05a75 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -397,6 +397,11 @@ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc)
{
unsigned int hdrlen = 24;
+ if (ieee80211_is_ext(fc)) {
+ hdrlen = 4;
+ goto out;
+ }
+
if (ieee80211_is_data(fc)) {
if (ieee80211_has_a4(fc))
hdrlen = 30;
--
2.20.1
Declare the structures needed to define S1G capabilities.
NL80211_ATTR_S1G_CAPABILITY can be passed along with
NL80211_ATTR_S1G_CAPABILITY_MASK to NL80211_CMD_ASSOCIATE
to indicate S1G capabilities which should override the
hardware capabilities in eg. the association request.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/net/cfg80211.h | 3 +++
include/uapi/linux/nl80211.h | 9 +++++++++
net/wireless/nl80211.c | 16 ++++++++++++++++
3 files changed, 28 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index f44839823d1e..b7e1a9dcf22c 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2486,6 +2486,8 @@ enum cfg80211_assoc_req_flags {
* @fils_nonces: FILS nonces (part of AAD) for protecting (Re)Association
* Request/Response frame or %NULL if FILS is not used. This field starts
* with 16 octets of STA Nonce followed by 16 octets of AP Nonce.
+ * @s1g_capa: S1G capability override
+ * @s1g_capa_mask: S1G capability override mask
*/
struct cfg80211_assoc_request {
struct cfg80211_bss *bss;
@@ -2500,6 +2502,7 @@ struct cfg80211_assoc_request {
const u8 *fils_kek;
size_t fils_kek_len;
const u8 *fils_nonces;
+ struct ieee80211_s1g_cap s1g_capa, s1g_capa_mask;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 00ac24f2e293..03f4d83da473 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2515,6 +2515,12 @@ enum nl80211_commands {
* @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
* association request when used with NL80211_CMD_NEW_STATION).
*
+ * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ * @NL80211_ATTR_S1G_CAPABILITY_MASK: S1G Capability Information element
+ * override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
+ * NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2997,6 +3003,8 @@ enum nl80211_attrs {
NL80211_ATTR_HE_6GHZ_CAPABILITY,
+ NL80211_ATTR_S1G_CAPABILITY,
+ NL80211_ATTR_S1G_CAPABILITY_MASK,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3046,6 +3054,7 @@ enum nl80211_attrs {
#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
#define NL80211_HT_CAPABILITY_LEN 26
#define NL80211_VHT_CAPABILITY_LEN 12
+#define NL80211_S1G_CAPABILITY_LEN 15
#define NL80211_HE_MIN_CAPABILITY_LEN 16
#define NL80211_HE_MAX_CAPABILITY_LEN 54
#define NL80211_MAX_NR_CIPHER_SUITES 5
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c5950d154d66..5672abb34b86 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -666,6 +666,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED },
[NL80211_ATTR_HE_6GHZ_CAPABILITY] =
NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)),
+ [NL80211_ATTR_S1G_CAPABILITY] = { .len = NL80211_S1G_CAPABILITY_LEN },
+ [NL80211_ATTR_S1G_CAPABILITY_MASK] = {
+ .len = NL80211_S1G_CAPABILITY_LEN },
};
/* policy for the key attributes */
@@ -9594,6 +9597,19 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
nla_data(info->attrs[NL80211_ATTR_FILS_NONCES]);
}
+ if (info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK])
+ memcpy(&req.s1g_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]),
+ sizeof(req.s1g_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&req.s1g_capa,
+ nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]),
+ sizeof(req.s1g_capa));
+ }
+
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err) {
wdev_lock(dev->ieee80211_ptr);
--
2.20.1
An S1G BSS can beacon at either 1 or 2 MHz and the channel
width is unique to a given frequency. Ignore scan channel
width for now and use the allowed channel width.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/scan.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5ac2785cdc7b..5002791fe165 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -905,6 +905,17 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
local->scan_chandef.center_freq1 = chan->center_freq;
local->scan_chandef.freq1_offset = chan->freq_offset;
local->scan_chandef.center_freq2 = 0;
+
+ /* For scanning on the S1G band, ignore scan_width (which is constant
+ * across all channels) for now since channel width is specific to each
+ * channel. Detect the required channel width here and likely revisit
+ * later. Maybe scan_width could be used to build the channel scan list?
+ */
+ if (chan->band == NL80211_BAND_S1GHZ) {
+ local->scan_chandef.width = ieee80211_s1g_channel_width(chan);
+ goto set_channel;
+ }
+
switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
@@ -925,8 +936,14 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
break;
+ case NL80211_BSS_CHAN_WIDTH_1:
+ case NL80211_BSS_CHAN_WIDTH_2:
+ /* shouldn't get here, S1G handled above */
+ WARN_ON(1);
+ break;
}
+set_channel:
if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
--
2.20.1
The S1G beacon is an extension frame as opposed to
management frame for the regular beacon. This means we may
have to occasionally cast the frame buffer to a different
header type. Luckily this isn't too bad as scan results
mostly only care about the IEs.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/linux/ieee80211.h | 32 ++++++++++++++++++++++
net/wireless/scan.c | 57 ++++++++++++++++++++++++++++++++-------
2 files changed, 80 insertions(+), 9 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index c47f43e65a2f..fe70cd99dec7 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -151,6 +151,9 @@
#define IEEE80211_ANO_NETTYPE_WILD 15
+/* bits unique to S1G beacon */
+#define IEEE80211_S1G_BCN_NEXT_TBTT 0x100
+
/* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */
#define IEEE80211_CTL_EXT_POLL 0x2000
#define IEEE80211_CTL_EXT_SPR 0x3000
@@ -553,6 +556,28 @@ static inline bool ieee80211_is_s1g_beacon(__le16 fc)
cpu_to_le16(IEEE80211_FTYPE_EXT | IEEE80211_STYPE_S1G_BEACON);
}
+/**
+ * ieee80211_next_tbtt_present - check if IEEE80211_FTYPE_EXT &&
+ * IEEE80211_STYPE_S1G_BEACON && IEEE80211_S1G_BCN_NEXT_TBTT
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_next_tbtt_present(__le16 fc)
+{
+ return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_EXT | IEEE80211_STYPE_S1G_BEACON) &&
+ fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT);
+}
+
+/**
+ * ieee80211_is_s1g_short_beacon - check if next tbtt present bit is set. Only
+ * true for S1G beacons when they're short.
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee80211_is_s1g_short_beacon(__le16 fc)
+{
+ return ieee80211_is_s1g_beacon(fc) && ieee80211_next_tbtt_present(fc);
+}
+
/**
* ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM
* @fc: frame control bytes in little-endian byteorder
@@ -1034,6 +1059,13 @@ struct ieee80211_ext {
u8 change_seq;
u8 variable[0];
} __packed s1g_beacon;
+ struct {
+ u8 sa[ETH_ALEN];
+ __le32 timestamp;
+ u8 change_seq;
+ u8 next_tbtt[3];
+ u8 variable[0];
+ } __packed s1g_short_beacon;
} u;
} __packed __aligned(2);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index e67a74488bbe..bf72a13dd2aa 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1807,8 +1807,11 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
bool signal_valid;
- size_t ielen = len - offsetof(struct ieee80211_mgmt,
- u.probe_resp.variable);
+ struct ieee80211_ext *ext = NULL;
+ u8 *bssid, *variable;
+ u16 capability, beacon_int;
+ size_t ielen, min_hdr_len = offsetof(struct ieee80211_mgmt,
+ u.probe_resp.variable);
int bss_type;
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
@@ -1826,21 +1829,57 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
(data->signal < 0 || data->signal > 100)))
return NULL;
- if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ ext = (void *) mgmt;
+ min_hdr_len = offsetof(struct ieee80211_ext, u.s1g_beacon);
+ if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
+ min_hdr_len = offsetof(struct ieee80211_ext,
+ u.s1g_short_beacon.variable);
+ }
+
+ if (WARN_ON(len < min_hdr_len))
return NULL;
- channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
+ ielen = len - min_hdr_len;
+ variable = mgmt->u.probe_resp.variable;
+ if (ext) {
+ if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
+ variable = ext->u.s1g_short_beacon.variable;
+ else
+ variable = ext->u.s1g_beacon.variable;
+ }
+
+ channel = cfg80211_get_bss_channel(wiphy, variable,
ielen, data->chan, data->scan_width);
if (!channel)
return NULL;
+ if (ext) {
+ struct ieee80211_s1g_bcn_compat_ie *compat;
+ u8 *ie;
+
+ ie = (void *)cfg80211_find_ie(WLAN_EID_S1G_BCN_COMPAT,
+ variable, ielen);
+ if (!ie)
+ return NULL;
+ compat = (void *)(ie + 2);
+ bssid = ext->u.s1g_beacon.sa;
+ capability = le16_to_cpu(compat->compat_info);
+ beacon_int = le16_to_cpu(compat->beacon_int);
+ } else {
+ bssid = mgmt->bssid;
+ beacon_int = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
+ capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ }
+
ies = kzalloc(sizeof(*ies) + ielen, gfp);
if (!ies)
return NULL;
ies->len = ielen;
ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
- ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
- memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
+ ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control) ||
+ ieee80211_is_s1g_beacon(mgmt->frame_control);
+ memcpy(ies->data, variable, ielen);
if (ieee80211_is_probe_resp(mgmt->frame_control))
rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
@@ -1848,12 +1887,12 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
rcu_assign_pointer(tmp.pub.ies, ies);
- memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
+ memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
+ tmp.pub.beacon_interval = beacon_int;
+ tmp.pub.capability = capability;
tmp.pub.channel = channel;
tmp.pub.scan_width = data->scan_width;
tmp.pub.signal = data->signal;
- tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
- tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
tmp.ts_boottime = data->boottime_ns;
tmp.parent_tsf = data->parent_tsf;
tmp.pub.chains = data->chains;
--
2.20.1
The sending STA type is implicit based on beacon or probe
response content. If sending STA was an S1G STA, adjust
the Information Element location accordingly.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/linux/ieee80211.h | 5 +++++
net/wireless/mlme.c | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index fe70cd99dec7..9307b60cef97 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1100,6 +1100,11 @@ struct ieee80211_mgmt {
/* followed by Supported rates */
u8 variable[0];
} __packed assoc_resp, reassoc_resp;
+ struct {
+ __le16 capab_info;
+ __le16 status_code;
+ u8 variable[0];
+ } __packed s1g_assoc_resp, s1g_reassoc_resp;
struct {
__le16 capab_info;
__le16 listen_interval;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index a6c61a2e6569..b5275fe93531 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -30,6 +30,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
struct cfg80211_connect_resp_params cr;
+ const struct cfg80211_bss_ies *ies;
+ const u8 *s1g;
memset(&cr, 0, sizeof(cr));
cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -42,6 +44,24 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
+ /* Detect whether this was an S1G Association Response and adjust IE
+ * location accordingly.
+ */
+ rcu_read_lock();
+ ies = rcu_dereference(bss->ies);
+ if (WARN_ON(!ies)) {
+ rcu_read_unlock();
+ return;
+ }
+ s1g = cfg80211_find_ie(WLAN_EID_S1G_CAPABILITIES, ies->data, ies->len);
+ if (s1g) {
+ cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
+ cr.resp_ie_len =
+ len - offsetof(struct ieee80211_mgmt,
+ u.s1g_assoc_resp.variable);
+ }
+ rcu_read_unlock();
+
trace_cfg80211_send_rx_assoc(dev, bss);
/*
--
2.20.1
Include the S1G Capabilities element in an association
request, and support the cfg80211 capability overrides.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/ieee80211_i.h | 4 ++++
net/mac80211/mlme.c | 8 +++++++
net/mac80211/util.c | 45 ++++++++++++++++++++++++++++++++++++++
3 files changed, 57 insertions(+)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0b1eaec6649f..5da2713af500 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -530,6 +530,8 @@ struct ieee80211_if_managed {
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+ struct ieee80211_s1g_cap s1g_capa; /* configured S1G overrides */
+ struct ieee80211_s1g_cap s1g_capa_mask; /* valid s1g_capa bits */
/* TDLS support */
u8 tdls_peer[ETH_ALEN] __aligned(2);
@@ -2193,6 +2195,8 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic,
enum nl80211_band band);
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
+u8 *ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_s1g_cap *caps, u8 *buf);
/* channel management */
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index ac870309b911..b69889563457 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1018,6 +1018,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
}
+ if (sband->band == NL80211_BAND_S1GHZ)
+ pos = ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap,
+ skb_put(skb, 17));
+
/* add any remaining custom (i.e. vendor specific here) IEs */
if (assoc_data->ie_len) {
noffset = assoc_data->ie_len;
@@ -5461,6 +5465,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask,
sizeof(ifmgd->vht_capa_mask));
+ memcpy(&ifmgd->s1g_capa, &req->s1g_capa, sizeof(ifmgd->s1g_capa));
+ memcpy(&ifmgd->s1g_capa_mask, &req->s1g_capa_mask,
+ sizeof(ifmgd->s1g_capa_mask));
+
if (req->ie && req->ie_len) {
memcpy(assoc_data->ie, req->ie, req->ie_len);
assoc_data->ie_len = req->ie_len;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 35798997e521..c7ab7ab057f9 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4278,6 +4278,51 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
return max_num_different_channels;
}
+u8 *ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_s1g_cap *own_cap, u8 *buf)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_s1g_cap s1g_capab;
+ u8 *pos = buf;
+ int i;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+ return pos;
+
+ if (!own_cap->s1g)
+ return pos;
+
+ memcpy(s1g_capab.capab_info, own_cap->cap, sizeof(own_cap->cap));
+ memcpy(s1g_capab.supp_mcs_nss, own_cap->nss_mcs,
+ sizeof(own_cap->nss_mcs));
+
+ /* override the capability info */
+ for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) {
+ u8 mask = ifmgd->s1g_capa_mask.capab_info[i];
+
+ s1g_capab.capab_info[i] &= ~mask;
+ s1g_capab.capab_info[i] |= (ifmgd->s1g_capa.capab_info[i] &
+ mask);
+ }
+
+ /* then MCS and NSS set */
+ for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) {
+ u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i];
+
+ s1g_capab.supp_mcs_nss[i] &= ~mask;
+ s1g_capab.supp_mcs_nss[i] |= (ifmgd->s1g_capa.supp_mcs_nss[i] &
+ mask);
+ }
+
+ *pos++ = WLAN_EID_S1G_CAPABILITIES;
+ *pos++ = sizeof(s1g_capab);
+
+ memcpy(pos, &s1g_capab, sizeof(s1g_capab));
+ pos += sizeof(s1g_capab);
+
+ return pos;
+}
+
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
{
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
--
2.20.1
Extract the BSS primary channel from the S1G Operation
element.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/wireless/scan.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index bf72a13dd2aa..169b6904e011 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1318,15 +1318,26 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
if (tmp && tmp[1] == 1) {
channel_number = tmp[2];
- } else {
- tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
- if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
- struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
+ goto found_channel;
+ }
- channel_number = htop->primary_chan;
- }
+ tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
+ if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
+ struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
+
+ channel_number = htop->primary_chan;
+ goto found_channel;
+ }
+
+ tmp = cfg80211_find_ie(WLAN_EID_S1G_OPERATION, ie, ielen);
+ if (tmp && tmp[1] >= sizeof(struct ieee80211_s1g_oper_ie)) {
+ struct ieee80211_s1g_oper_ie *s1gop = (void *)(tmp + 2);
+
+ channel_number = s1gop->primary_ch;
+ goto found_channel;
}
+found_channel:
if (channel_number < 0) {
/* No channel information in frame payload */
return channel;
--
2.20.1
For now just skip the duration calculation for frames
transmitted on the S1G band and avoid a warning.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/tx.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index dca01d7e6e3e..424fd9b2aeda 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -82,6 +82,10 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
erp = txrate->flags & IEEE80211_RATE_ERP_G;
+ /* TODO */
+ if (sband->band == NL80211_BAND_S1GHZ)
+ return 0;
+
/*
* data and mgmt (except PS Poll):
* - during CFP: 32768
--
2.20.1
This commit finds the correct offset for Information
Elements in S1G beacon frames so they can be reported in
scan results, and track whether a given BSS is S1G.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/ieee80211_i.h | 7 +++++++
net/mac80211/rx.c | 3 ++-
net/mac80211/scan.c | 20 ++++++++++++++++----
net/mac80211/util.c | 28 ++++++++++++++++++++++++++++
4 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5da2713af500..378945b0f3a0 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -127,6 +127,9 @@ struct ieee80211_bss {
/* Keep track of what bits of information we have valid info for. */
u8 valid_data;
+
+ /* BSS info was transmitted by an S1G STA */
+ u8 s1g;
};
/**
@@ -1524,6 +1527,10 @@ struct ieee802_11_elems {
u8 dtim_count;
u8 dtim_period;
const struct ieee80211_addba_ext_ie *addba_ext_ie;
+ const struct ieee80211_s1g_cap *s1g_capab;
+ const struct ieee80211_s1g_oper_ie *s1g_oper;
+ const u8 *s1g_tsbtt;
+ const struct ieee80211_s1g_bcn_compat_ie *s1g_bcn_compat;
/* length of them, respectively */
u8 ext_capab_len;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 836cde516a18..5b92f56682e2 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4586,7 +4586,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_verify_alignment(&rx);
if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) ||
- ieee80211_is_beacon(hdr->frame_control)))
+ ieee80211_is_beacon(hdr->frame_control) ||
+ ieee80211_is_s1g_beacon(hdr->frame_control)))
ieee80211_scan_rx(local, skb);
if (ieee80211_is_data(fc)) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5002791fe165..3dd65b7c839b 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -124,6 +124,9 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_WMM;
}
+ if (!elems->parse_error && elems->s1g_capab)
+ bss->s1g = true;
+
if (beacon) {
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[rx_status->band];
@@ -146,7 +149,8 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_channel *channel)
{
- bool beacon = ieee80211_is_beacon(mgmt->frame_control);
+ bool beacon = ieee80211_is_beacon(mgmt->frame_control) ||
+ ieee80211_is_s1g_beacon(mgmt->frame_control);
struct cfg80211_bss *cbss, *non_tx_cbss;
struct ieee80211_bss *bss, *non_tx_bss;
struct cfg80211_inform_bss bss_meta = {
@@ -195,6 +199,11 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
elements = mgmt->u.probe_resp.variable;
baselen = offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
+ } else if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ struct ieee80211_ext *ext = (void *) mgmt;
+
+ baselen = offsetof(struct ieee80211_ext, u.s1g_beacon.variable);
+ elements = ext->u.s1g_beacon.variable;
} else {
baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
elements = mgmt->u.beacon.variable;
@@ -246,9 +255,12 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
struct ieee80211_bss *bss;
struct ieee80211_channel *channel;
- if (skb->len < 24 ||
- (!ieee80211_is_probe_resp(mgmt->frame_control) &&
- !ieee80211_is_beacon(mgmt->frame_control)))
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ if (skb->len < 15)
+ return;
+ } else if (skb->len < 24 ||
+ (!ieee80211_is_probe_resp(mgmt->frame_control) &&
+ !ieee80211_is_beacon(mgmt->frame_control)))
return;
sdata1 = rcu_dereference(local->scan_sdata);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index c7ab7ab057f9..0fa57349c4e5 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1003,6 +1003,10 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
case WLAN_EID_LINK_ID:
case WLAN_EID_BSS_MAX_IDLE_PERIOD:
case WLAN_EID_RSNX:
+ case WLAN_EID_S1G_BCN_COMPAT:
+ case WLAN_EID_S1G_CAPABILITIES:
+ case WLAN_EID_S1G_OPERATION:
+ case WLAN_EID_S1G_SHORT_BCN_INTERVAL:
/*
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
* that if the content gets bigger it might be needed more than once
@@ -1288,6 +1292,30 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
&crc : NULL,
elem, elems);
break;
+ case WLAN_EID_S1G_CAPABILITIES:
+ if (elen == 15)
+ elems->s1g_capab = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
+ case WLAN_EID_S1G_OPERATION:
+ if (elen == 6)
+ elems->s1g_oper = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
+ case WLAN_EID_S1G_SHORT_BCN_INTERVAL:
+ if (elen == 2)
+ elems->s1g_tsbtt = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
+ case WLAN_EID_S1G_BCN_COMPAT:
+ if (elen == sizeof(struct ieee80211_s1g_bcn_compat_ie))
+ elems->s1g_bcn_compat = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
default:
break;
}
--
2.20.1
S1G doesn't have legacy (sband->bitrates) rates, only MCS.
For now, just send a frame at MCS 0 if a low rate is
requested. Note we also redefine (since we're out of TX
flags) TX_RC_VHT_MCS as TX_RC_S1G_MCS to indicate an S1G
MCS. This is probably OK as VHT MCS is not valid on S1G
band and vice versa.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/net/mac80211.h | 2 ++
net/mac80211/rate.c | 33 ++++++++++++++++++++++++++-------
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 66e2bfd165e8..a9340e673610 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -812,6 +812,8 @@ enum mac80211_tx_info_flags {
#define IEEE80211_TX_CTL_STBC_SHIFT 23
+#define IEEE80211_TX_RC_S1G_MCS IEEE80211_TX_RC_VHT_MCS
+
/**
* enum mac80211_tx_control_flags - flags to describe transmit control
*
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index b051f125d3af..63266d73c252 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -266,10 +266,15 @@ void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata)
if (WARN_ON(!sdata->vif.bss_conf.chandef.chan))
return;
+ band = sdata->vif.bss_conf.chandef.chan->band;
+ if (band == NL80211_BAND_S1GHZ) {
+ /* TODO */
+ return;
+ }
+
if (WARN_ON_ONCE(!basic_rates))
return;
- band = sdata->vif.bss_conf.chandef.chan->band;
user_mask = sdata->rc_rateidx_mask[band];
sband = local->hw.wiphy->bands[band];
@@ -296,21 +301,29 @@ static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
!ieee80211_is_data(fc);
}
-static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
+static void rc_send_low_basicrate(struct ieee80211_tx_rate *rate,
+ u32 basic_rates,
struct ieee80211_supported_band *sband)
{
u8 i;
+ if (sband->band == NL80211_BAND_S1GHZ) {
+ /* TODO */
+ rate->flags |= IEEE80211_TX_RC_S1G_MCS;
+ rate->idx = 0;
+ return;
+ }
+
if (basic_rates == 0)
return; /* assume basic rates unknown and accept rate */
- if (*idx < 0)
+ if (rate->idx < 0)
return;
- if (basic_rates & (1 << *idx))
+ if (basic_rates & (1 << rate->idx))
return; /* selected rate is a basic rate */
- for (i = *idx + 1; i <= sband->n_bitrates; i++) {
+ for (i = rate->idx + 1; i <= sband->n_bitrates; i++) {
if (basic_rates & (1 << i)) {
- *idx = i;
+ rate->idx = i;
return;
}
}
@@ -328,6 +341,12 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
u32 rate_flags =
ieee80211_chandef_rate_flags(&hw->conf.chandef);
+ if (sband->band == NL80211_BAND_S1GHZ) {
+ info->control.rates[0].flags |= IEEE80211_TX_RC_S1G_MCS;
+ info->control.rates[0].idx = 0;
+ return;
+ }
+
if ((sband->band == NL80211_BAND_2GHZ) &&
(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
rate_flags |= IEEE80211_RATE_ERP_G;
@@ -388,7 +407,7 @@ static bool rate_control_send_low(struct ieee80211_sta *pubsta,
}
if (use_basicrate)
- rc_send_low_basicrate(&info->control.rates[0].idx,
+ rc_send_low_basicrate(&info->control.rates[0],
txrc->bss_conf->basic_rates,
sband);
--
2.20.1
S1G allows listen interval up to 2^14 * 10000 beacon
intervals. In order to do this listen interval needs a
scaling factor applied to the lower 14 bits. Calculate
this and properly encode the listen interval for S1G STAs.
See IEEE802.11ah-2016 Table 9-44a for reference.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/linux/ieee80211.h | 8 ++++++++
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 11 +++++++----
net/mac80211/util.c | 20 ++++++++++++++++++++
4 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 9307b60cef97..93c1496af8f2 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -2446,6 +2446,14 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
#define S1G_CAPAB_B9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0)
+#define LISTEN_INT_USF_MASK (BIT(14) | BIT(15))
+#define LISTEN_INT_USF_SHIFT 14
+
+#define IEEE80211_MAX_USF 3
+#define IEEE80211_MAX_UI ((1 << 14) - 1)
+
+static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 378945b0f3a0..8b779ad363c5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2293,6 +2293,7 @@ void ieee80211_tdls_chsw_work(struct work_struct *wk);
void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
const u8 *peer, u16 reason);
const char *ieee80211_get_reason_code_string(u16 reason_code);
+__le16 ieee80211_encode_usf(int val);
extern const struct ethtool_ops ieee80211_ethtool_ops;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b69889563457..6a62a221b89e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -686,6 +686,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
+ struct ieee80211_bss *bss = (void *)assoc_data->bss->priv;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u8 *pos, qos_info, *ie_start;
@@ -696,6 +697,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
u32 rates = 0;
+ __le16 listen_int;
struct element *ext_capa = NULL;
/* we know it's writable, cast away the const */
@@ -784,13 +786,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN);
+ listen_int = cpu_to_le16(bss->s1g ?
+ ieee80211_encode_usf(local->hw.conf.listen_interval) :
+ local->hw.conf.listen_interval);
if (!is_zero_ether_addr(assoc_data->prev_bssid)) {
skb_put(skb, 10);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_REASSOC_REQ);
mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval =
- cpu_to_le16(local->hw.conf.listen_interval);
+ mgmt->u.reassoc_req.listen_interval = listen_int;
memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid,
ETH_ALEN);
} else {
@@ -798,8 +802,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.assoc_req.listen_interval =
- cpu_to_le16(local->hw.conf.listen_interval);
+ mgmt->u.assoc_req.listen_interval = listen_int;
}
/* SSID */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0fa57349c4e5..caf4098deeda 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4393,3 +4393,23 @@ const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = {
IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
IEEE80211_WMM_IE_STA_QOSINFO_AC_BK
};
+
+__le16 ieee80211_encode_usf(int listen_interval)
+{
+ u16 ui, usf = 0;
+
+ /* find greatest USF */
+ while (usf < IEEE80211_MAX_USF) {
+ if (listen_interval % listen_int_usf[usf + 1])
+ break;
+ usf += 1;
+ }
+ ui = listen_interval / listen_int_usf[usf];
+
+ /* error if there is a remainder. Should've been checked by user */
+ if (WARN_ON_ONCE(ui > IEEE80211_MAX_UI))
+ ui &= (1 << LISTEN_INT_USF_SHIFT) - 1;
+ listen_interval = usf << LISTEN_INT_USF_SHIFT | ui;
+
+ return listen_interval;
+}
--
2.20.1
minstrel_ht is confused by the lack of sband->bitrates,
and S1G will likely require a unique RC algorithm, so
avoid rate init for now if STA is on the S1G band.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/rate.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 63266d73c252..0cba7fed28cf 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -51,6 +51,12 @@ void rate_control_rate_init(struct sta_info *sta)
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+ /* TODO: check for minstrel_s1g ? */
+ if (sband->band == NL80211_BAND_S1GHZ) {
+ rcu_read_unlock();
+ return;
+ }
+
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
--
2.20.1
S1G beacons are 802.11 Extension Frames, so the fixed
header part differs from regular beacons.
Add a handler to process S1G beacons and abstract out the
fetching of BSSID and element start locations in the
beacon body handler.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/ieee80211_i.h | 4 ++
net/mac80211/iface.c | 5 +++
net/mac80211/mlme.c | 87 +++++++++++++++++++++++++++++---------
net/mac80211/rx.c | 84 ++++++++++++++++--------------------
net/mac80211/util.c | 52 +++++++++++++++++++++++
5 files changed, 164 insertions(+), 68 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8b779ad363c5..5d0157065db6 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1649,6 +1649,8 @@ int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
+void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
@@ -2294,6 +2296,8 @@ void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
const u8 *peer, u16 reason);
const char *ieee80211_get_reason_code_string(u16 reason_code);
__le16 ieee80211_encode_usf(int val);
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
+ enum nl80211_iftype type);
extern const struct ethtool_ops ieee80211_ethtool_ops;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 9740ae8fa697..1369f6e575d7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1379,6 +1379,11 @@ static void ieee80211_iface_work(struct work_struct *work)
WARN_ON(1);
break;
}
+ } else if (ieee80211_is_ext(mgmt->frame_control)) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_sta_rx_queued_ext(sdata, skb);
+ else
+ WARN_ON(1);
} else if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *)mgmt;
/*
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 6a62a221b89e..9a26ef99cef9 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1604,6 +1604,9 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
int new_ap_level;
__le16 capab = mgmt->u.probe_resp.capab_info;
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control))
+ return 0; /* TODO */
+
if (country_ie &&
(capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
@@ -2450,7 +2453,8 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
* data idle periods for sending the periodic probe request to the
* AP we're connected to.
*/
- if (is_multicast_ether_addr(hdr->addr1))
+ if (is_multicast_ether_addr(hdr->addr1) ||
+ ieee80211_is_s1g_beacon(hdr->frame_control))
return;
ieee80211_sta_reset_conn_monitor(sdata);
@@ -3919,12 +3923,13 @@ static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
}
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
+static void ieee80211_rx_beacon(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr, size_t len,
+ struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+ struct ieee80211_mgmt *mgmt = (void *) hdr;
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
@@ -3934,14 +3939,24 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
u32 changed = 0;
bool erp_valid;
u8 erp_value = 0;
- u32 ncrc;
- u8 *bssid;
+ u32 ncrc = 0;
+ u8 *bssid, *variable = mgmt->u.beacon.variable;
u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
sdata_assert_lock(sdata);
/* Process beacon from the current BSS */
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+ bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ struct ieee80211_ext *ext = (void *) mgmt;
+
+ if (ieee80211_is_s1g_short_beacon(ext->frame_control))
+ variable = ext->u.s1g_short_beacon.variable;
+ else
+ variable = ext->u.s1g_beacon.variable;
+ }
+
+ baselen = (u8 *) variable - (u8 *) mgmt;
if (baselen > len)
return;
@@ -3961,10 +3976,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
- ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->assoc_data->bss)) {
- ieee802_11_parse_elems(mgmt->u.beacon.variable,
+ ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
+ ieee802_11_parse_elems(variable,
len - baselen, false, &elems,
- mgmt->bssid,
+ bssid,
ifmgd->assoc_data->bss->bssid);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status);
@@ -3997,7 +4012,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
if (!ifmgd->associated ||
- !ieee80211_rx_our_beacon(mgmt->bssid, ifmgd->associated))
+ !ieee80211_rx_our_beacon(bssid, ifmgd->associated))
return;
bssid = ifmgd->associated->bssid;
@@ -4017,8 +4032,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
*/
ieee80211_sta_reset_beacon_monitor(sdata);
- ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
- ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
+ /* TODO: CRC urrently not calculated on S1G Beacon Compatibility
+ * element (which carries the beacon interval). Don't forget to add a
+ * bit to care_about_ies[] above if mac80211 is interested in a
+ * changing S1G element.
+ */
+ if (!ieee80211_is_s1g_beacon(hdr->frame_control))
+ ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
+ ncrc = ieee802_11_parse_elems_crc(variable,
len - baselen, false, &elems,
care_about_ies, ncrc,
mgmt->bssid, bssid);
@@ -4052,7 +4073,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_p2p_noa_attr noa = {};
int ret;
- ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
+ ret = cfg80211_get_p2p_attr(variable,
len - baselen,
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
(u8 *) &noa, sizeof(noa));
@@ -4088,7 +4109,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
* the driver will use them. The synchronized view is currently
* guaranteed only in certain callbacks.
*/
- if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
+ if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) &&
+ !ieee80211_is_s1g_beacon(hdr->frame_control)) {
sdata->vif.bss_conf.sync_tsf =
le64_to_cpu(mgmt->u.beacon.timestamp);
sdata->vif.bss_conf.sync_device_ts =
@@ -4096,7 +4118,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.sync_dtim_count = elems.dtim_count;
}
- if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
+ if ((ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) ||
+ ieee80211_is_s1g_short_beacon(mgmt->frame_control))
return;
ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true;
@@ -4137,9 +4160,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
} else {
erp_valid = false;
}
- changed |= ieee80211_handle_bss_capability(sdata,
- le16_to_cpu(mgmt->u.beacon.capab_info),
- erp_valid, erp_value);
+
+ if (!ieee80211_is_s1g_beacon(hdr->frame_control)) {
+ changed |= ieee80211_handle_bss_capability(sdata,
+ le16_to_cpu(mgmt->u.beacon.capab_info),
+ erp_valid, erp_value);
+ }
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, bssid);
@@ -4177,6 +4203,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata, changed);
}
+void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ sdata_lock(sdata);
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_S1G_BEACON:
+ ieee80211_rx_beacon(sdata, hdr, skb->len, rx_status);
+ break;
+ }
+ sdata_unlock(sdata);
+}
+
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
@@ -4194,7 +4240,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
+ ieee80211_rx_beacon(sdata, (struct ieee80211_hdr *) mgmt,
+ skb->len, rx_status);
break;
case IEEE80211_STYPE_PROBE_RESP:
ieee80211_rx_mgmt_probe_resp(sdata, skb);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5b92f56682e2..e1291898bde8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -42,51 +42,6 @@ static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
u64_stats_update_end(&tstats->syncp);
}
-static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
- enum nl80211_iftype type)
-{
- __le16 fc = hdr->frame_control;
-
- if (ieee80211_is_data(fc)) {
- if (len < 24) /* drop incorrect hdr len (data) */
- return NULL;
-
- if (ieee80211_has_a4(fc))
- return NULL;
- if (ieee80211_has_tods(fc))
- return hdr->addr1;
- if (ieee80211_has_fromds(fc))
- return hdr->addr2;
-
- return hdr->addr3;
- }
-
- if (ieee80211_is_mgmt(fc)) {
- if (len < 24) /* drop incorrect hdr len (mgmt) */
- return NULL;
- return hdr->addr3;
- }
-
- if (ieee80211_is_ctl(fc)) {
- if (ieee80211_is_pspoll(fc))
- return hdr->addr1;
-
- if (ieee80211_is_back_req(fc)) {
- switch (type) {
- case NL80211_IFTYPE_STATION:
- return hdr->addr2;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- return hdr->addr1;
- default:
- break; /* fall through to the return */
- }
- }
- }
-
- return NULL;
-}
-
/*
* monitor mode reception
*
@@ -1801,7 +1756,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
}
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
sta->rx_stats.last_rx = jiffies;
- } else if (!is_multicast_ether_addr(hdr->addr1)) {
+ } else if (!ieee80211_is_s1g_beacon(hdr->frame_control) &&
+ is_multicast_ether_addr(hdr->addr1)) {
/*
* Mesh beacons will update last_rx when if they are found to
* match the current local configuration when processed.
@@ -1839,6 +1795,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
}
}
+ if (ieee80211_is_s1g_beacon(hdr->frame_control))
+ return RX_CONTINUE;
+
/*
* Change STA power saving mode only at the end of a frame
* exchange sequence, and only for a data or management
@@ -1949,6 +1908,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
__le16 fc;
const struct ieee80211_cipher_scheme *cs = NULL;
+ if (ieee80211_is_ext(hdr->frame_control))
+ return RX_CONTINUE;
+
/*
* Key selection 101
*
@@ -2257,7 +2219,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control;
- if (ieee80211_is_ctl(fc))
+ if (ieee80211_is_ctl(fc) || ieee80211_is_ext(fc))
return RX_CONTINUE;
sc = le16_to_cpu(hdr->seq_ctrl);
@@ -3131,6 +3093,9 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control))
+ return RX_CONTINUE;
+
/*
* From here on, look only at management frames.
* Data and control frames are already handled,
@@ -3597,6 +3562,27 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_hdr *hdr = (void *)rx->skb->data;
+
+ if (!ieee80211_is_ext(hdr->frame_control))
+ return RX_CONTINUE;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return RX_DROP_MONITOR;
+
+ /* for now only beacons are ext, so queue them */
+ skb_queue_tail(&sdata->skb_queue, rx->skb);
+ ieee80211_queue_work(&rx->local->hw, &sdata->work);
+ if (rx->sta)
+ rx->sta->rx_stats.packets++;
+
+ return RX_QUEUED;
+}
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
@@ -3816,6 +3802,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
CALL_RXH(ieee80211_rx_h_userspace_mgmt);
CALL_RXH(ieee80211_rx_h_action_post_userspace);
CALL_RXH(ieee80211_rx_h_action_return);
+ CALL_RXH(ieee80211_rx_h_ext);
CALL_RXH(ieee80211_rx_h_mgmt);
rxh_next:
@@ -3982,7 +3969,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
- bool multicast = is_multicast_ether_addr(hdr->addr1);
+ bool multicast = is_multicast_ether_addr(hdr->addr1) ||
+ ieee80211_is_s1g_beacon(hdr->frame_control);
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index caf4098deeda..0d304c328f7e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -45,6 +45,58 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
}
EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
+ enum nl80211_iftype type)
+{
+ __le16 fc = hdr->frame_control;
+
+ if (ieee80211_is_data(fc)) {
+ if (len < 24) /* drop incorrect hdr len (data) */
+ return NULL;
+
+ if (ieee80211_has_a4(fc))
+ return NULL;
+ if (ieee80211_has_tods(fc))
+ return hdr->addr1;
+ if (ieee80211_has_fromds(fc))
+ return hdr->addr2;
+
+ return hdr->addr3;
+ }
+
+ if (ieee80211_is_s1g_beacon(fc)) {
+ struct ieee80211_ext *ext = (void *) hdr;
+
+ return ext->u.s1g_beacon.sa;
+ }
+
+ if (ieee80211_is_mgmt(fc)) {
+ if (len < 24) /* drop incorrect hdr len (mgmt) */
+ return NULL;
+ return hdr->addr3;
+ }
+
+ if (ieee80211_is_ctl(fc)) {
+ if (ieee80211_is_pspoll(fc))
+ return hdr->addr1;
+
+ if (ieee80211_is_back_req(fc)) {
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ return hdr->addr2;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ return hdr->addr1;
+ default:
+ break; /* fall through to the return */
+ }
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(ieee80211_get_bssid);
+
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb;
--
2.20.1
The changes required for associating in S1G are:
- apply S1G BSS channel info before assoc
- mark all S1G STAs as QoS STAs
- include and parse AID request element
- handle new Association Response format
- don't fail assoc if supported rates element is missing
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/linux/ieee80211.h | 25 ++++++++++++
include/net/mac80211.h | 1 +
net/mac80211/cfg.c | 2 +
net/mac80211/ibss.c | 3 +-
net/mac80211/ieee80211_i.h | 4 ++
net/mac80211/mlme.c | 80 ++++++++++++++++++++++++++++++++------
net/mac80211/util.c | 56 ++++++++++++++++++++++++++
7 files changed, 158 insertions(+), 13 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 93c1496af8f2..5cff6a373158 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -987,6 +987,25 @@ enum ieee80211_vht_opmode_bits {
IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF = 0x80,
};
+/**
+ * enum ieee80211_s1g_chanwidth
+ * These are defined in IEEE802.11-2016ah Table 10-20
+ * as BSS Channel Width
+ *
+ * @IEEE80211_S1G_CHANWIDTH_1MHZ: 1MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_2MHZ: 2MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_4MHZ: 4MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_8MHZ: 8MHz operating channel
+ * @IEEE80211_S1G_CHANWIDTH_16MHZ: 16MHz operating channel
+ */
+enum ieee80211_s1g_chanwidth {
+ IEEE80211_S1G_CHANWIDTH_1MHZ = 0,
+ IEEE80211_S1G_CHANWIDTH_2MHZ = 1,
+ IEEE80211_S1G_CHANWIDTH_4MHZ = 3,
+ IEEE80211_S1G_CHANWIDTH_8MHZ = 7,
+ IEEE80211_S1G_CHANWIDTH_16MHZ = 15,
+};
+
#define WLAN_SA_QUERY_TR_ID_LEN 2
#define WLAN_MEMBERSHIP_LEN 8
#define WLAN_USER_POSITION_LEN 16
@@ -2446,6 +2465,10 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
#define S1G_CAPAB_B9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0)
+#define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ BIT(0)
+#define S1G_OPER_CH_WIDTH_OPER (BIT(1) | BIT(2) | BIT(3) | BIT(4))
+#define S1G_OPER_CH_WIDTH_OPER_SHIFT 1
+
#define LISTEN_INT_USF_MASK (BIT(14) | BIT(15))
#define LISTEN_INT_USF_SHIFT 14
@@ -2853,6 +2876,8 @@ enum ieee80211_eid {
WLAN_EID_REDUCED_NEIGHBOR_REPORT = 201,
+ WLAN_EID_AID_REQUEST = 210,
+ WLAN_EID_AID_RESPONSE = 211,
WLAN_EID_S1G_BCN_COMPAT = 213,
WLAN_EID_S1G_SHORT_BCN_INTERVAL = 214,
WLAN_EID_S1G_CAPABILITIES = 217,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a9340e673610..c5760a912ec5 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -674,6 +674,7 @@ struct ieee80211_bss_conf {
} he_oper;
struct ieee80211_he_obss_pd he_obss_pd;
struct cfg80211_he_bss_color he_bss_color;
+ bool s1g;
};
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 87fddd84c621..bbd9577788fb 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1071,6 +1071,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sizeof(struct ieee80211_he_obss_pd));
memcpy(&sdata->vif.bss_conf.he_bss_color, ¶ms->he_bss_color,
sizeof(struct ieee80211_he_bss_color));
+ sdata->vif.bss_conf.s1g = params->chandef.chan->band ==
+ NL80211_BAND_S1GHZ;
sdata->vif.bss_conf.ssid_len = params->ssid_len;
if (params->ssid_len)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 53632c2f5217..a8a87ce2d9da 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1037,7 +1037,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
}
if (sta && !sta->sta.wme &&
- elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS) {
+ (elems->wmm_info || elems->s1g_capab) &&
+ local->hw.queues >= IEEE80211_NUM_ACS) {
sta->sta.wme = true;
ieee80211_check_fast_xmit(sta);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5d0157065db6..c218e2294869 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1531,6 +1531,7 @@ struct ieee802_11_elems {
const struct ieee80211_s1g_oper_ie *s1g_oper;
const u8 *s1g_tsbtt;
const struct ieee80211_s1g_bcn_compat_ie *s1g_bcn_compat;
+ const struct ieee80211_aid_response_ie *aid_resp;
/* length of them, respectively */
u8 ext_capab_len;
@@ -2206,6 +2207,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
u8 *ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_s1g_cap *caps, u8 *buf);
+u8 *ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, u8 *buf);
/* channel management */
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
@@ -2217,6 +2219,8 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_he_operation *he_oper,
struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
+ struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9a26ef99cef9..2476d4a9596c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -149,6 +149,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
+ const struct ieee80211_s1g_oper_ie *s1g_oper,
struct cfg80211_chan_def *chandef, bool tracking)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -176,6 +177,15 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+ if (s1g_oper && sband->band == NL80211_BAND_S1GHZ) {
+ ieee80211_chandef_s1g_oper(s1g_oper, chandef);
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_VHT |
+ IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ goto out;
+ }
+
if (!ht_oper || !sta_ht_cap.ht_supported) {
ret = IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_VHT |
@@ -347,6 +357,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
+ const struct ieee80211_s1g_oper_ie *s1g_oper,
const u8 *bssid, u32 *changed)
{
struct ieee80211_local *local = sdata->local;
@@ -393,7 +404,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
/* calculate new channel (type) based on HT/VHT/HE operation IEs */
flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
ht_oper, vht_oper, he_oper,
- &chandef, true);
+ s1g_oper, &chandef, true);
/*
* Downgrade the new channel if we associated with restricted
@@ -812,6 +823,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = assoc_data->ssid_len;
memcpy(pos, assoc_data->ssid, assoc_data->ssid_len);
+ if (bss->s1g)
+ goto skip_rates;
+
/* add all rates which were marked to be used above */
supp_rates_len = rates_len;
if (supp_rates_len > 8)
@@ -847,6 +861,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
+skip_rates:
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT ||
capab & WLAN_CAPABILITY_RADIO_MEASURE) {
pos = skb_put(skb, 4);
@@ -1021,9 +1036,11 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
}
- if (sband->band == NL80211_BAND_S1GHZ)
+ if (sband->band == NL80211_BAND_S1GHZ) {
+ pos = ieee80211_add_aid_request_ie(sdata, skb_put(skb, 3));
pos = ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap,
skb_put(skb, 17));
+ }
/* add any remaining custom (i.e. vendor specific here) IEs */
if (assoc_data->ie_len) {
@@ -3270,6 +3287,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_bss *bss = (void *)cbss->priv;
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
@@ -3279,13 +3297,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
u32 changed = 0;
+ u8 *pos;
int err;
bool ret;
/* AssocResp and ReassocResp have identical structure */
+ pos = mgmt->u.assoc_resp.variable;
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+ if (bss->s1g) {
+ pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
+ aid = 0; /* TODO */
+ }
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+ ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems,
+ mgmt->bssid, assoc_data->bss->bssid);
+
+ if (elems->aid_resp)
+ aid = le16_to_cpu(elems->aid_resp->aid);
/*
* The 5 MSB of the AID field are reserved
@@ -3302,7 +3331,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ifmgd->broken_ap = true;
}
- if (!elems->supp_rates) {
+ if (!bss->s1g && !elems->supp_rates) {
sdata_info(sdata, "no SuppRates element in AssocResp\n");
return false;
}
@@ -3544,7 +3573,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
sta->sta.mfp = false;
}
- sta->sta.wme = elems->wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
+ sta->sta.wme = (elems->wmm_param || elems->s1g_capab) &&
+ local->hw.queues >= IEEE80211_NUM_ACS;
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@@ -3636,7 +3666,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
int ac, uapsd_queues = -1;
u8 *pos;
bool reassoc;
- struct cfg80211_bss *bss;
+ struct cfg80211_bss *cbss;
+ struct ieee80211_bss *bss;
struct ieee80211_event event = {
.type = MLME_EVENT,
.u.mlme.data = ASSOC_EVENT,
@@ -3646,9 +3677,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!assoc_data)
return;
+
if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
return;
+ cbss = assoc_data->bss;
+ bss = (void *) cbss->priv;
+
/*
* AssocResp and ReassocResp have identical structure, so process both
* of them in this function.
@@ -3660,7 +3695,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control);
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+ pos = mgmt->u.assoc_resp.variable;
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+ if (bss->s1g) {
+ pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
+ aid = 0; /* TODO */
+ }
sdata_info(sdata,
"RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
@@ -3671,7 +3711,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
return;
- pos = mgmt->u.assoc_resp.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems,
mgmt->bssid, assoc_data->bss->bssid);
@@ -3691,8 +3730,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return;
}
- bss = assoc_data->bss;
-
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
@@ -3701,10 +3738,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
} else {
- if (!ieee80211_assoc_success(sdata, bss, mgmt, len, &elems)) {
+ if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, &elems)) {
/* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, false, false);
- cfg80211_assoc_timeout(sdata->dev, bss);
+ cfg80211_assoc_timeout(sdata->dev, cbss);
return;
}
event.u.mlme.status = MLME_SUCCESS;
@@ -3725,7 +3762,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
}
- cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues,
+ cfg80211_rx_assoc_resp(sdata->dev, cbss, (u8 *)mgmt, len, uapsd_queues,
ifmgd->assoc_req_ies, ifmgd->assoc_req_ies_len);
}
@@ -4175,7 +4212,7 @@ static void ieee80211_rx_beacon(struct ieee80211_sub_if_data *sdata,
if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
elems.vht_cap_elem, elems.ht_operation,
elems.vht_operation, elems.he_operation,
- bssid, &changed)) {
+ elems.s1g_oper, bssid, &changed)) {
mutex_unlock(&local->sta_mtx);
sdata_info(sdata,
"failed to follow AP %pM bandwidth change, disconnect\n",
@@ -4912,6 +4949,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_operation *ht_oper = NULL;
const struct ieee80211_vht_operation *vht_oper = NULL;
const struct ieee80211_he_operation *he_oper = NULL;
+ const struct ieee80211_s1g_oper_ie *s1g_oper = NULL;
struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef;
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
@@ -5014,10 +5052,23 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
if (!have_80mhz)
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+ if (sband->band == NL80211_BAND_S1GHZ) {
+ const u8 *s1g_oper_ie;
+
+ s1g_oper_ie = ieee80211_bss_get_ie(cbss,
+ WLAN_EID_S1G_OPERATION);
+ if (s1g_oper_ie && s1g_oper_ie[1] >= sizeof(*s1g_oper))
+ s1g_oper = (void *)(s1g_oper_ie + 2);
+ else
+ sdata_info(sdata,
+ "AP missing S1G operation element?\n");
+ }
+
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
cbss->channel,
bss->vht_cap_info,
ht_oper, vht_oper, he_oper,
+ s1g_oper,
&chandef, false);
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
@@ -5144,6 +5195,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_bss_ies *ies;
int shift = ieee80211_vif_get_shift(&sdata->vif);
+ /* TODO: S1G Basic Rate Set is expressed elsewhere */
+ if (cbss->channel->band == NL80211_BAND_S1GHZ)
+ goto skip_rates;
+
ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
@@ -5188,6 +5243,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+skip_rates:
memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN);
/* set timing information */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0d304c328f7e..2153b65297c4 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1058,6 +1058,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
case WLAN_EID_S1G_BCN_COMPAT:
case WLAN_EID_S1G_CAPABILITIES:
case WLAN_EID_S1G_OPERATION:
+ case WLAN_EID_AID_RESPONSE:
case WLAN_EID_S1G_SHORT_BCN_INTERVAL:
/*
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
@@ -1368,6 +1369,12 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
else
elem_parse_failed = true;
break;
+ case WLAN_EID_AID_RESPONSE:
+ if (elen == sizeof(struct ieee80211_aid_response_ie))
+ elems->aid_resp = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
default:
break;
}
@@ -3452,6 +3459,43 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
*chandef = he_chandef;
+ return false;
+}
+
+bool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper,
+ struct cfg80211_chan_def *chandef)
+{
+ u32 oper_freq;
+
+ if (!oper)
+ return false;
+
+ switch ((oper->ch_width & S1G_OPER_CH_WIDTH_OPER) >>
+ S1G_OPER_CH_WIDTH_OPER_SHIFT) {
+ case IEEE80211_S1G_CHANWIDTH_1MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_1;
+ break;
+ case IEEE80211_S1G_CHANWIDTH_2MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_2;
+ break;
+ case IEEE80211_S1G_CHANWIDTH_4MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_4;
+ break;
+ case IEEE80211_S1G_CHANWIDTH_8MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_8;
+ break;
+ case IEEE80211_S1G_CHANWIDTH_16MHZ:
+ chandef->width = NL80211_CHAN_WIDTH_16;
+ break;
+ default:
+ return false;
+ }
+
+ oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch,
+ NL80211_BAND_S1GHZ);
+ chandef->center_freq1 = KHZ_TO_MHZ(oper_freq);
+ chandef->freq1_offset = oper_freq % 1000;
+
return true;
}
@@ -4358,6 +4402,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
return max_num_different_channels;
}
+u8 *ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, u8 *buf)
+{
+ u8 *pos = buf;
+
+ *pos++ = WLAN_EID_AID_REQUEST;
+ *pos++ = 1;
+
+ *pos++ = 0;
+
+ return pos;
+}
+
u8 *ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_s1g_cap *own_cap, u8 *buf)
{
--
2.20.1
Advertise S1G Capabilities and channels to mac80211.
Requires a few fixups to account for missing
sband->bitrates, and a custom regulatory db to actually
enable the S1G channels.
Signed-off-by: Thomas Pedersen <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 80 ++++++++++++++++++++++++---
include/linux/ieee80211.h | 12 +++-
2 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9dd9d73f4484..ee2f3a008e01 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -377,6 +377,50 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = {
CHAN5G(5925), /* Channel 185 */
};
+#define NUM_S1G_CHANS_US 51
+static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US];
+
+static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = {
+ .s1g = true,
+ .cap = { S1G_CAPAB_B0_SGI_1MHZ | S1G_CAPAB_B0_SGI_2MHZ |
+ SM(S1G_CAPAB_B0_SUPP_CH_WIDTH, S1G_SUPP_CH_WIDTH_2),
+ 0,
+ 0,
+ S1G_CAPAB_B3_MAX_MPDU_LEN,
+ 0,
+ S1G_CAPAB_B5_AMPDU,
+ 0,
+ S1G_CAPAB_B7_DUP_1MHZ,
+ S1G_CAPAB_B8_TWT_RESPOND | S1G_CAPAB_B8_TWT_REQUEST,
+ 0},
+ .nss_mcs = { 0xfc | 1, /* MCS 7 for 1 SS */
+ /* RX Highest Supported Long GI Data Rate 0:7 */
+ 0,
+ /* RX Highest Supported Long GI Data Rate 0:7 */
+ /* TX S1G MCS Map 0:6 */
+ 0xfa,
+ /* TX S1G MCS Map :7 */
+ /* TX Highest Supported Long GI Data Rate 0:6 */
+ 0x80,
+ /* TX Highest Supported Long GI Data Rate 7:8 */
+ /* Rx Single spatial stream and S1G-MCS Map for 1MHz */
+ /* Tx Single spatial stream and S1G-MCS Map for 1MHz */
+ 0 },
+};
+
+static void hwsim_init_s1g_channels(struct ieee80211_channel *channels)
+{
+ int ch, freq;
+
+ for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) {
+ freq = 902000 + (ch + 1) * 500;
+ channels[ch].band = NL80211_BAND_S1GHZ;
+ channels[ch].center_freq = KHZ_TO_MHZ(freq);
+ channels[ch].freq_offset = freq % 1000;
+ channels[ch].hw_value = ch + 1;
+ }
+}
+
static const struct ieee80211_rate hwsim_rates[] = {
{ .bitrate = 10 },
{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
@@ -505,6 +549,7 @@ struct mac80211_hwsim_data {
struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
+ struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)];
struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
struct ieee80211_iface_combination if_combination;
struct ieee80211_iface_limit if_limits[3];
@@ -900,12 +945,14 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct mac80211_hwsim_data *data = hw->priv;
struct sk_buff *skb;
struct hwsim_radiotap_hdr *hdr;
- u16 flags;
+ u16 flags, bitrate;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb);
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
- if (WARN_ON(!txrate))
- return;
+ if (!txrate)
+ bitrate = 0;
+ else
+ bitrate = txrate->bitrate;
if (!netif_running(hwsim_mon))
return;
@@ -924,10 +971,10 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
(1 << IEEE80211_RADIOTAP_CHANNEL));
hdr->rt_tsft = __mac80211_hwsim_get_tsf(data);
hdr->rt_flags = 0;
- hdr->rt_rate = txrate->bitrate / 5;
+ hdr->rt_rate = bitrate / 5;
hdr->rt_channel = cpu_to_le16(chan->center_freq);
flags = IEEE80211_CHAN_2GHZ;
- if (txrate->flags & IEEE80211_RATE_ERP_G)
+ if (txrate && txrate->flags & IEEE80211_RATE_ERP_G)
flags |= IEEE80211_CHAN_OFDM;
else
flags |= IEEE80211_CHAN_CCK;
@@ -1341,6 +1388,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
memset(&rx_status, 0, sizeof(rx_status));
rx_status.flag |= RX_FLAG_MACTIME_START;
rx_status.freq = chan->center_freq;
+ rx_status.freq_offset = chan->freq_offset ? 1 : 0;
rx_status.band = chan->band;
if (info->control.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
rx_status.rate_idx =
@@ -1522,14 +1570,18 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
/* fake header transmission time */
struct ieee80211_mgmt *mgmt;
struct ieee80211_rate *txrate;
+ /* TODO: get MCS */
+ int bitrate = 100;
u64 ts;
mgmt = (struct ieee80211_mgmt *)skb->data;
txrate = ieee80211_get_tx_rate(hw, txi);
+ if (txrate)
+ bitrate = txrate->bitrate;
ts = mac80211_hwsim_get_tsf_raw();
mgmt->u.probe_resp.timestamp =
cpu_to_le64(ts + data->tsf_offset +
- 24 * 8 * 10 / txrate->bitrate);
+ 24 * 8 * 10 / bitrate);
}
mac80211_hwsim_monitor_rx(hw, skb, channel);
@@ -1664,6 +1716,8 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_rate *txrate;
struct ieee80211_mgmt *mgmt;
struct sk_buff *skb;
+ /* TODO: get MCS */
+ int bitrate = 100;
hwsim_check_magic(vif);
@@ -1683,13 +1737,15 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
ARRAY_SIZE(info->control.rates));
txrate = ieee80211_get_tx_rate(hw, info);
+ if (txrate)
+ bitrate = txrate->bitrate;
mgmt = (struct ieee80211_mgmt *) skb->data;
/* fake header transmission time */
data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw();
mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts +
data->tsf_offset +
- 24 * 8 * 10 / txrate->bitrate);
+ 24 * 8 * 10 / bitrate);
mac80211_hwsim_tx_frame(hw, skb,
rcu_dereference(vif->chanctx_conf)->def.chan);
@@ -3079,6 +3135,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
sizeof(hwsim_channels_2ghz));
memcpy(data->channels_5ghz, hwsim_channels_5ghz,
sizeof(hwsim_channels_5ghz));
+ memcpy(data->channels_s1g, hwsim_channels_s1g,
+ sizeof(hwsim_channels_s1g));
memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
@@ -3121,6 +3179,12 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
sband->vht_cap.vht_mcs.tx_mcs_map =
sband->vht_cap.vht_mcs.rx_mcs_map;
break;
+ case NL80211_BAND_S1GHZ:
+ memcpy(&sband->s1g_cap, &hwsim_s1g_cap,
+ sizeof(sband->s1g_cap));
+ sband->channels = data->channels_s1g;
+ sband->n_channels = ARRAY_SIZE(hwsim_channels_s1g);
+ break;
default:
continue;
}
@@ -4318,6 +4382,8 @@ static int __init init_mac80211_hwsim(void)
goto out_exit_virtio;
}
+ hwsim_init_s1g_channels(hwsim_channels_s1g);
+
for (i = 0; i < radios; i++) {
struct hwsim_new_radio_params param = { 0 };
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 5cff6a373158..e2198453ecb6 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -2385,6 +2385,9 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
return spr_len;
}
+#define SM(f, v) (((v) << f##_SHIFT) & f)
+#define MS(f, v) (((v) & f) >> f##_SHIFT)
+
/* S1G Capabilities Information field */
#define S1G_CAPAB_B0_S1G_LONG BIT(0)
#define S1G_CAPAB_B0_SGI_1MHZ BIT(1)
@@ -2392,9 +2395,16 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
#define S1G_CAPAB_B0_SGI_4MHZ BIT(3)
#define S1G_CAPAB_B0_SGI_8MHZ BIT(4)
#define S1G_CAPAB_B0_SGI_16MHZ BIT(5)
-#define S1G_CAPAB_B0_SUPP_CH_WIDTH_MASK (BIT(6) | BIT(7))
+#define S1G_CAPAB_B0_SUPP_CH_WIDTH (BIT(6) | BIT(7))
#define S1G_CAPAB_B0_SUPP_CH_WIDTH_SHIFT 6
+#define S1G_SUPP_CH_WIDTH_2 0
+#define S1G_SUPP_CH_WIDTH_4 1
+#define S1G_SUPP_CH_WIDTH_8 2
+#define S1G_SUPP_CH_WIDTH_16 3
+#define S1G_SUPP_CH_WIDTH_MAX(cap) ((1 << MS(S1G_CAPAB_B0_SUPP_CH_WIDTH, \
+ cap[0])) << 1)
+
#define S1G_CAPAB_B1_RX_LDPC BIT(0)
#define S1G_CAPAB_B1_TX_STBC BIT(1)
#define S1G_CAPAB_B1_RX_STBC BIT(2)
--
2.20.1
S1G channels have a minimum bandwidth of 1Mhz, and there
is a 1:1 mapping of allowed bandwidth to channel number.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/util.c | 6 ++--
net/wireless/nl80211.c | 3 +-
net/wireless/reg.c | 69 ++++++++++++++++++++++++++++++++++--------
3 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 9ba1afe4ba1f..35798997e521 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1437,7 +1437,7 @@ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx_conf *chanctx_conf;
const struct ieee80211_reg_rule *rrule;
const struct ieee80211_wmm_ac *wmm_ac;
- u16 center_freq = 0;
+ u16 min_bw, center_freq = 0;
if (sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_STATION)
@@ -1453,7 +1453,9 @@ void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
return;
}
- rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq), 20);
+ min_bw = chanctx_conf->def.chan->band == NL80211_BAND_S1GHZ ? 1 : 20;
+ rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq),
+ min_bw);
if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
rcu_read_unlock();
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index dd0f62e942ed..a57872708108 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1012,9 +1012,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
goto nla_put_failure;
if (large) {
+ u32 min_bw = chan->band == NL80211_BAND_S1GHZ ? 1 : 20;
const struct ieee80211_reg_rule *rule =
freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
- 20);
+ min_bw);
if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
if (nl80211_msg_put_wmm_rules(msg, rule))
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6c130cec22c3..f23ca3103558 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1617,9 +1617,11 @@ __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
{
const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
const struct ieee80211_reg_rule *reg_rule = NULL;
+ const u32 bws[] = {1, 2, 4, 5, 8, 10, 16, 20};
+ int i = sizeof(bws) / sizeof(u32) - 1;
u32 bw;
- for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
+ for (bw = MHZ_TO_KHZ(bws[i]); bw >= min_bw; bw = MHZ_TO_KHZ(bws[i--])) {
reg_rule = freq_reg_info_regd(center_freq, regd, bw);
if (!IS_ERR(reg_rule))
return reg_rule;
@@ -1660,6 +1662,7 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
{
const struct ieee80211_freq_range *freq_range = NULL;
u32 max_bandwidth_khz, center_freq_khz, bw_flags = 0;
+ bool is_s1g = chan->band == NL80211_BAND_S1GHZ;
freq_range = ®_rule->freq_range;
@@ -1679,16 +1682,57 @@ static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ if (is_s1g) {
+ /* S1G is strict about non overlapping channels. We can
+ * calculate which bandwidth is allowed per channel by finding
+ * the largest bandwidth which cleanly divides the freq_range.
+ */
+ int edge_offset;
+ int ch_bw = max_bandwidth_khz;
+
+ while (ch_bw) {
+ edge_offset = (center_freq_khz - ch_bw / 2) -
+ freq_range->start_freq_khz;
+ if (edge_offset % ch_bw == 0) {
+ switch (KHZ_TO_MHZ(ch_bw)) {
+ case 1:
+ bw_flags |= IEEE80211_CHAN_1MHZ;
+ break;
+ case 2:
+ bw_flags |= IEEE80211_CHAN_2MHZ;
+ break;
+ case 4:
+ bw_flags |= IEEE80211_CHAN_4MHZ;
+ break;
+ case 8:
+ bw_flags |= IEEE80211_CHAN_8MHZ;
+ break;
+ case 16:
+ bw_flags |= IEEE80211_CHAN_16MHZ;
+ break;
+ default:
+ /* If we got here, no bandwidths fit on
+ * this frequency, ie. band edge.
+ */
+ bw_flags |= IEEE80211_CHAN_DISABLED;
+ break;
+ }
+ break;
+ }
+ ch_bw /= 2;
+ }
+ } else {
+ if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ }
return bw_flags;
}
@@ -1707,12 +1751,13 @@ static void handle_channel(struct wiphy *wiphy,
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
const struct ieee80211_regdomain *regd;
+ u32 min_bw = chan->band == NL80211_BAND_S1GHZ ? 1 : 20;
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
flags = chan->orig_flags;
- reg_rule = freq_reg_info(wiphy, ieee80211_channel_to_khz(chan), 20);
+ reg_rule = freq_reg_info(wiphy, ieee80211_channel_to_khz(chan), min_bw);
if (IS_ERR(reg_rule)) {
/*
* We will disable all channels that do not match our
--
2.20.1
mac80211_hwsim was corrupting the S1G beacon because the
timestamp location (and size) does not match the
management beacon.
Signed-off-by: Thomas Pedersen <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index ee2f3a008e01..f53b144c431b 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1743,9 +1743,18 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
mgmt = (struct ieee80211_mgmt *) skb->data;
/* fake header transmission time */
data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw();
- mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts +
- data->tsf_offset +
- 24 * 8 * 10 / bitrate);
+ if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+ struct ieee80211_ext *ext = (void *) mgmt;
+
+ ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts +
+ data->tsf_offset +
+ 10 * 8 * 10 /
+ bitrate);
+ } else {
+ mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts +
+ data->tsf_offset +
+ 24 * 8 * 10 / bitrate);
+ }
mac80211_hwsim_tx_frame(hw, skb,
rcu_dereference(vif->chanctx_conf)->def.chan);
--
2.20.1
Until now, the wifi default channels were assumed to be
NL80211_CHAN_NO_HT, or NL80211_CHAN_WIDTH_20_NOHT. S1G
devices however do not support these channel types/width.
When a default channel width is requested (during default
chandef init, or chanctx removal when not using channel
context), for S1G calculate the correct width. Fixes eg.
configuring strange (20Mhz) width during a scan on the S1G
band.
Signed-off-by: Thomas Pedersen <[email protected]>
---
net/mac80211/chan.c | 9 ++++++++-
net/wireless/chan.c | 10 ++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index bdc0f29dc6cd..8f48aff74c7b 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -536,7 +536,14 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
if (!local->use_chanctx) {
struct cfg80211_chan_def *chandef = &local->_oper_chandef;
- chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ /* S1G doesn't have 20MHz, so get the correct width for the
+ * current channel.
+ */
+ if (chandef->chan->band == NL80211_BAND_S1GHZ)
+ chandef->width =
+ ieee80211_s1g_channel_width(chandef->chan);
+ else
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = chandef->chan->center_freq;
chandef->freq1_offset = chandef->chan->freq_offset;
chandef->center_freq2 = 0;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 0a4b8d3eaed9..3bff52d51b05 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -32,6 +32,16 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
chandef->edmg.bw_config = 0;
chandef->edmg.channels = 0;
+ /* S1G allows a single width per channel, and since chan_type seems to
+ * be for backwards compatibility only, ignore it and return the per
+ * frequency width.
+ */
+ if (chan->band == NL80211_BAND_S1GHZ) {
+ chandef->width = ieee80211_s1g_channel_width(chan);
+ chandef->center_freq1 = chan->center_freq;
+ return;
+ }
+
switch (chan_type) {
case NL80211_CHAN_NO_HT:
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
--
2.20.1
Recently channels gained a potential frequency offset, so
include this in the per-channel survey info.
Signed-off-by: Thomas Pedersen <[email protected]>
---
include/uapi/linux/nl80211.h | 2 ++
net/wireless/nl80211.c | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 03f4d83da473..973b56c2226f 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4073,6 +4073,7 @@ enum nl80211_user_reg_hint_type {
* receiving frames destined to the local BSS
* @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
* currently defined
+ * @NL80211_SURVEY_INFO_FREQUENCY_OFFSET: center frequency offset in KHz
* @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
*/
enum nl80211_survey_info {
@@ -4088,6 +4089,7 @@ enum nl80211_survey_info {
NL80211_SURVEY_INFO_TIME_SCAN,
NL80211_SURVEY_INFO_PAD,
NL80211_SURVEY_INFO_TIME_BSS_RX,
+ NL80211_SURVEY_INFO_FREQUENCY_OFFSET,
/* keep last */
__NL80211_SURVEY_INFO_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5672abb34b86..750bef58b319 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -9119,6 +9119,11 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
survey->channel->center_freq))
goto nla_put_failure;
+ if (survey->channel &&
+ nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY_OFFSET,
+ survey->channel->freq_offset))
+ goto nla_put_failure;
+
if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
goto nla_put_failure;
--
2.20.1
Hi Thomas,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on next-20200827]
[also build test ERROR on v5.9-rc2]
[cannot apply to mac80211-next/master mac80211/master wireless-drivers-next/master wireless-drivers/master v5.9-rc2 v5.9-rc1 v5.8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-initial-S1G-support/20200828-063630
base: 88abac0b753dfdd85362a26d2da8277cb1e0842b
config: riscv-allyesconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=riscv
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All errors (new ones prefixed by >>):
drivers/net/wireless/realtek/rtw88/regd.c: In function 'rtw_regd_apply_beaconing_flags':
>> drivers/net/wireless/realtek/rtw88/regd.c:279:15: error: too few arguments to function 'freq_reg_info'
279 | reg_rule = freq_reg_info(wiphy,
| ^~~~~~~~~~~~~
In file included from include/net/mac80211.h:21,
from drivers/net/wireless/realtek/rtw88/main.h:8,
from drivers/net/wireless/realtek/rtw88/regd.c:5:
include/net/cfg80211.h:5880:34: note: declared here
5880 | const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
| ^~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/dma-mapping.h:7,
from include/linux/skbuff.h:31,
from include/linux/if_ether.h:19,
from include/net/mac80211.h:18,
from drivers/net/wireless/realtek/rtw88/main.h:8,
from drivers/net/wireless/realtek/rtw88/regd.c:5:
At top level:
include/linux/sched/topology.h:40:3: warning: 'sd_flag_debug' defined but not used [-Wunused-const-variable=]
40 | } sd_flag_debug[] = {
| ^~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/dma-mapping.h:7,
from include/linux/skbuff.h:31,
from include/linux/if_ether.h:19,
from include/net/mac80211.h:18,
from drivers/net/wireless/realtek/rtw88/main.h:8,
from drivers/net/wireless/realtek/rtw88/regd.c:5:
include/linux/sched/topology.h:30:27: warning: 'SD_DEGENERATE_GROUPS_MASK' defined but not used [-Wunused-const-variable=]
30 | static const unsigned int SD_DEGENERATE_GROUPS_MASK =
| ^~~~~~~~~~~~~~~~~~~~~~~~~
# https://github.com/0day-ci/linux/commit/cc241e6d10cc9d9bef4ab40d2989677df2623bd9
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Thomas-Pedersen/add-initial-S1G-support/20200828-063630
git checkout cc241e6d10cc9d9bef4ab40d2989677df2623bd9
vim +/freq_reg_info +279 drivers/net/wireless/realtek/rtw88/regd.c
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 261
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 262 static void rtw_regd_apply_beaconing_flags(struct wiphy *wiphy,
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 263 enum nl80211_reg_initiator initiator)
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 264 {
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 265 enum nl80211_band band;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 266 struct ieee80211_supported_band *sband;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 267 const struct ieee80211_reg_rule *reg_rule;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 268 struct ieee80211_channel *ch;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 269 unsigned int i;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 270
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 271 for (band = 0; band < NUM_NL80211_BANDS; band++) {
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 272 if (!wiphy->bands[band])
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 273 continue;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 274
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 275 sband = wiphy->bands[band];
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 276 for (i = 0; i < sband->n_channels; i++) {
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 277 ch = &sband->channels[i];
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 278
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 @279 reg_rule = freq_reg_info(wiphy,
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 280 MHZ_TO_KHZ(ch->center_freq));
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 281 if (IS_ERR(reg_rule))
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 282 continue;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 283
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 284 ch->flags &= ~IEEE80211_CHAN_DISABLED;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 285
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 286 if (!(reg_rule->flags & NL80211_RRF_NO_IR))
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 287 ch->flags &= ~IEEE80211_CHAN_NO_IR;
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 288 }
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 289 }
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 290 }
e3037485c68ec1a Yan-Hsuan Chuang 2019-04-26 291
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
On 2020-08-27 15:32, Thomas Pedersen wrote:
> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
> index 35b8847a2f6d..6c130cec22c3 100644
> --- a/net/wireless/reg.c
> +++ b/net/wireless/reg.c
> @@ -1629,9 +1629,10 @@ __freq_reg_info(struct wiphy *wiphy, u32
> center_freq, u32 min_bw)
> }
>
> const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
> - u32 center_freq)
> + u32 center_freq,
> + u32 min_bw)
> {
> - return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
> + return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(min_bw));
> }
> EXPORT_SYMBOL(freq_reg_info);
Actually, it would be cleaner to keep the freq_reg_info() interface
intact,
then set min_bw = 1 if center_freq is in the S1G band. The call to
freq_reg_info() for successively smaller bandwidths in the next patch
could
just call __freq_reg_info() directly. Will fix in v2.
--
thomas
Hi Thomas,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on next-20200827]
[cannot apply to mac80211-next/master mac80211/master wireless-drivers-next/master wireless-drivers/master v5.9-rc2 v5.9-rc1 v5.8 v5.9-rc2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-initial-S1G-support/20200828-063630
base: 88abac0b753dfdd85362a26d2da8277cb1e0842b
config: riscv-allyesconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=riscv
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
All warnings (new ones prefixed by >>):
In file included from drivers/net/wireless/ath/ath10k/sdio.c:17:
>> drivers/net/wireless/ath/ath10k/core.h:31: warning: "MS" redefined
31 | #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath10k/htt.h:16,
from drivers/net/wireless/ath/ath10k/core.h:18,
from drivers/net/wireless/ath/ath10k/sdio.c:17:
include/linux/ieee80211.h:2389: note: this is the location of the previous definition
2389 | #define MS(f, v) (((v) & f) >> f##_SHIFT)
|
In file included from drivers/net/wireless/ath/ath10k/sdio.c:17:
>> drivers/net/wireless/ath/ath10k/core.h:32: warning: "SM" redefined
32 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath10k/htt.h:16,
from drivers/net/wireless/ath/ath10k/core.h:18,
from drivers/net/wireless/ath/ath10k/sdio.c:17:
include/linux/ieee80211.h:2388: note: this is the location of the previous definition
2388 | #define SM(f, v) (((v) << f##_SHIFT) & f)
|
include/linux/ieee80211.h:2488:18: warning: 'listen_int_usf' defined but not used [-Wunused-const-variable=]
2488 | static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
| ^~~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/mmc/card.h:10,
from drivers/net/wireless/ath/ath10k/sdio.c:9:
include/linux/sched/topology.h:40:3: warning: 'sd_flag_debug' defined but not used [-Wunused-const-variable=]
40 | } sd_flag_debug[] = {
| ^~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/mmc/card.h:10,
from drivers/net/wireless/ath/ath10k/sdio.c:9:
include/linux/sched/topology.h:30:27: warning: 'SD_DEGENERATE_GROUPS_MASK' defined but not used [-Wunused-const-variable=]
30 | static const unsigned int SD_DEGENERATE_GROUPS_MASK =
| ^~~~~~~~~~~~~~~~~~~~~~~~~
--
In file included from drivers/net/wireless/ath/ath10k/trace.h:10,
from drivers/net/wireless/ath/ath10k/trace.c:9:
>> drivers/net/wireless/ath/ath10k/core.h:31: warning: "MS" redefined
31 | #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath10k/htt.h:16,
from drivers/net/wireless/ath/ath10k/core.h:18,
from drivers/net/wireless/ath/ath10k/trace.h:10,
from drivers/net/wireless/ath/ath10k/trace.c:9:
include/linux/ieee80211.h:2389: note: this is the location of the previous definition
2389 | #define MS(f, v) (((v) & f) >> f##_SHIFT)
|
In file included from drivers/net/wireless/ath/ath10k/trace.h:10,
from drivers/net/wireless/ath/ath10k/trace.c:9:
>> drivers/net/wireless/ath/ath10k/core.h:32: warning: "SM" redefined
32 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath10k/htt.h:16,
from drivers/net/wireless/ath/ath10k/core.h:18,
from drivers/net/wireless/ath/ath10k/trace.h:10,
from drivers/net/wireless/ath/ath10k/trace.c:9:
include/linux/ieee80211.h:2388: note: this is the location of the previous definition
2388 | #define SM(f, v) (((v) << f##_SHIFT) & f)
|
--
In file included from drivers/net/wireless/ath/ath11k/mhi.c:7:
>> drivers/net/wireless/ath/ath11k/core.h:27: warning: "SM" redefined
27 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath11k/wmi.h:9,
from drivers/net/wireless/ath/ath11k/core.h:15,
from drivers/net/wireless/ath/ath11k/mhi.c:7:
include/linux/ieee80211.h:2388: note: this is the location of the previous definition
2388 | #define SM(f, v) (((v) << f##_SHIFT) & f)
|
include/linux/ieee80211.h:2488:18: warning: 'listen_int_usf' defined but not used [-Wunused-const-variable=]
2488 | static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
| ^~~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/pci.h:37,
from drivers/net/wireless/ath/ath11k/mhi.c:5:
include/linux/sched/topology.h:40:3: warning: 'sd_flag_debug' defined but not used [-Wunused-const-variable=]
40 | } sd_flag_debug[] = {
| ^~~~~~~~~~~~~
In file included from include/linux/energy_model.h:10,
from include/linux/device.h:16,
from include/linux/pci.h:37,
from drivers/net/wireless/ath/ath11k/mhi.c:5:
include/linux/sched/topology.h:30:27: warning: 'SD_DEGENERATE_GROUPS_MASK' defined but not used [-Wunused-const-variable=]
30 | static const unsigned int SD_DEGENERATE_GROUPS_MASK =
| ^~~~~~~~~~~~~~~~~~~~~~~~~
--
In file included from drivers/net/wireless/ath/ath11k/core.c:10:
>> drivers/net/wireless/ath/ath11k/core.h:27: warning: "SM" redefined
27 | #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
|
In file included from include/net/mac80211.h:20,
from drivers/net/wireless/ath/ath11k/wmi.h:9,
from drivers/net/wireless/ath/ath11k/core.h:15,
from drivers/net/wireless/ath/ath11k/core.c:10:
include/linux/ieee80211.h:2388: note: this is the location of the previous definition
2388 | #define SM(f, v) (((v) << f##_SHIFT) & f)
|
# https://github.com/0day-ci/linux/commit/dc5ef7078b77772b5e2ff5a57cd87144c4c9a583
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Thomas-Pedersen/add-initial-S1G-support/20200828-063630
git checkout dc5ef7078b77772b5e2ff5a57cd87144c4c9a583
vim +/MS +31 drivers/net/wireless/ath/ath10k/core.h
5e3dd157d7e70f0 Kalle Valo 2013-06-12 30
5e3dd157d7e70f0 Kalle Valo 2013-06-12 @31 #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
5e3dd157d7e70f0 Kalle Valo 2013-06-12 @32 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
5e3dd157d7e70f0 Kalle Valo 2013-06-12 33 #define WO(_f) ((_f##_OFFSET) >> 2)
5e3dd157d7e70f0 Kalle Valo 2013-06-12 34
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Thomas Pedersen <[email protected]> writes:
> Advertise S1G Capabilities and channels to mac80211.
>
> Requires a few fixups to account for missing
> sband->bitrates, and a custom regulatory db to actually
> enable the S1G channels.
>
> Signed-off-by: Thomas Pedersen <[email protected]>
> ---
> drivers/net/wireless/mac80211_hwsim.c | 80 ++++++++++++++++++++++++---
> include/linux/ieee80211.h | 12 +++-
IMHO a mac80211_hwsim patch should not touch ieee80211.h.
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -2385,6 +2385,9 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
> return spr_len;
> }
>
> +#define SM(f, v) (((v) << f##_SHIFT) & f)
> +#define MS(f, v) (((v) & f) >> f##_SHIFT)
> +
> /* S1G Capabilities Information field */
> #define S1G_CAPAB_B0_S1G_LONG BIT(0)
> #define S1G_CAPAB_B0_SGI_1MHZ BIT(1)
> @@ -2392,9 +2395,16 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
> #define S1G_CAPAB_B0_SGI_4MHZ BIT(3)
> #define S1G_CAPAB_B0_SGI_8MHZ BIT(4)
> #define S1G_CAPAB_B0_SGI_16MHZ BIT(5)
> -#define S1G_CAPAB_B0_SUPP_CH_WIDTH_MASK (BIT(6) | BIT(7))
> +#define S1G_CAPAB_B0_SUPP_CH_WIDTH (BIT(6) | BIT(7))
> #define S1G_CAPAB_B0_SUPP_CH_WIDTH_SHIFT 6
>
> +#define S1G_SUPP_CH_WIDTH_2 0
> +#define S1G_SUPP_CH_WIDTH_4 1
> +#define S1G_SUPP_CH_WIDTH_8 2
> +#define S1G_SUPP_CH_WIDTH_16 3
> +#define S1G_SUPP_CH_WIDTH_MAX(cap) ((1 << MS(S1G_CAPAB_B0_SUPP_CH_WIDTH, \
> + cap[0])) << 1)
We have GENMASK(), FIELD_GET() & co nowadays so no need for custom
macros.
--
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Greeting,
FYI, we noticed the following commit (built with gcc-9):
commit: dc5ef7078b77772b5e2ff5a57cd87144c4c9a583 ("mac80211_hwsim: indicate support for S1G")
url: https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-initial-S1G-support/20200828-063630
in testcase: boot
on test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 8G
caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):
+-------------------------------------------------+------------+------------+
| | d86026ca9a | dc5ef7078b |
+-------------------------------------------------+------------+------------+
| boot_successes | 8 | 0 |
| boot_failures | 0 | 8 |
| BUG:KASAN:stack-out-of-bounds_in__freq_reg_info | 0 | 8 |
+-------------------------------------------------+------------+------------+
If you fix the issue, kindly add following tag
Reported-by: kernel test robot <[email protected]>
[ 16.018498] BUG: KASAN: stack-out-of-bounds in __freq_reg_info+0x14b/0x170
[ 16.019402] Read of size 4 at addr ffffc9000001f8e4 by task swapper/0/1
[ 16.020281]
[ 16.020387] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.9.0-rc2-next-20200827-00021-gdc5ef7078b7777 #1
[ 16.020387] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
[ 16.020387] Call Trace:
[ 16.020387] dump_stack+0xbf/0x110
[ 16.020387] print_address_description.cold+0x5/0x4b3
[ 16.020387] ? log_store.cold+0x11/0x11
[ 16.020387] ? trace_hardirqs_off+0x29/0x110
[ 16.020387] ? _raw_spin_lock_irqsave+0x8c/0xd0
[ 16.020387] ? _raw_read_lock_irq+0x50/0x50
[ 16.020387] ? trace_hardirqs_on+0x2e/0x120
[ 16.020387] ? __freq_reg_info+0x14b/0x170
[ 16.020387] kasan_report.cold+0x1f/0x38
[ 16.020387] ? __freq_reg_info+0x14b/0x170
[ 16.020387] __freq_reg_info+0x14b/0x170
[ 16.020387] ? freq_reg_info_regd+0x110/0x110
[ 16.020387] ? mutex_is_locked+0x1b/0x30
[ 16.020387] wiphy_update_regulatory+0x338/0x6b0
[ 16.020387] wiphy_regulatory_register+0x3b/0xa0
[ 16.020387] wiphy_register+0xcf3/0x1020
[ 16.020387] ? wiphy_unregister+0x570/0x570
[ 16.020387] ? register_netdev+0x40/0x40
[ 16.020387] ? minstrel_ht_alloc+0x1a7/0x230
[ 16.020387] ieee80211_register_hw+0xf05/0x1460
[ 16.020387] ? ieee80211_ifa6_changed+0x230/0x230
[ 16.020387] ? memset+0x20/0x40
[ 16.020387] ? __hrtimer_init+0xbb/0xf0
[ 16.020387] mac80211_hwsim_new_radio+0xd89/0x1830
[ 16.020387] ? hwsim_virtio_rx_work+0x1f0/0x1f0
[ 16.020387] init_mac80211_hwsim+0x315/0x42c
[ 16.020387] ? printk+0x96/0xb2
[ 16.020387] ? rndis_wlan_driver_init+0x1a/0x1a
[ 16.020387] do_one_initcall+0x75/0x294
[ 16.020387] ? perf_trace_initcall_level+0x1f0/0x1f0
[ 16.020387] ? parameqn+0x80/0x90
[ 16.020387] ? kasan_unpoison_shadow+0x33/0x40
[ 16.020387] kernel_init_freeable+0x2b8/0x313
[ 16.020387] ? rest_init+0xd6/0xd6
[ 16.020387] kernel_init+0xd/0x11a
[ 16.020387] ret_from_fork+0x22/0x30
[ 16.020387]
[ 16.020387] addr ffffc9000001f8e4 is located in stack of task swapper/0/1 at offset 28 in frame:
[ 16.020387] __freq_reg_info+0x0/0x170
[ 16.020387]
[ 16.020387] this frame has 1 object:
[ 16.020387] [32, 64) 'bws'
[ 16.020387]
[ 16.020387] Memory state around the buggy address:
[ 16.020387] ffffc9000001f780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 16.020387] ffffc9000001f800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 16.020387] >ffffc9000001f880: 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00
[ 16.020387] ^
[ 16.020387] ffffc9000001f900: 00 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00
[ 16.020387] ffffc9000001f980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 16.020387] ==================================================================
[ 16.020387] Disabling lock debugging due to kernel taint
[ 16.065939] ieee80211 phy1: Selected rate control algorithm 'minstrel_ht'
[ 16.073468] usbcore: registered new interface driver catc
[ 16.074373] usbcore: registered new interface driver kaweth
[ 16.075147] pegasus: v0.9.3 (2013/04/25), Pegasus/Pegasus II USB Ethernet driver
[ 16.076356] usbcore: registered new interface driver pegasus
[ 16.077286] usbcore: registered new interface driver rtl8150
[ 16.078066] hso: drivers/net/usb/hso.c: Option Wireless
[ 16.079118] usbcore: registered new interface driver hso
[ 16.080014] usbcore: registered new interface driver lan78xx
[ 16.080941] usbcore: registered new interface driver cdc_ether
[ 16.081861] usbcore: registered new interface driver cdc_eem
[ 16.082776] usbcore: registered new interface driver dm9601
[ 16.083759] usbcore: registered new interface driver CoreChips
[ 16.084773] usbcore: registered new interface driver smsc95xx
[ 16.085703] usbcore: registered new interface driver gl620a
[ 16.086632] usbcore: registered new interface driver net1080
[ 16.087543] usbcore: registered new interface driver rndis_host
[ 16.088503] usbcore: registered new interface driver cdc_subset
[ 16.089446] usbcore: registered new interface driver kalmia
[ 16.090365] usbcore: registered new interface driver ipheth
[ 16.091315] usbcore: registered new interface driver sierra_net
[ 16.092269] usbcore: registered new interface driver cx82310_eth
[ 16.093306] usbcore: registered new interface driver cdc_ncm
[ 16.094224] usbcore: registered new interface driver huawei_cdc_ncm
[ 16.095202] usbcore: registered new interface driver lg-vl600
[ 16.096151] usbcore: registered new interface driver qmi_wwan
[ 16.097078] usbcore: registered new interface driver cdc_mbim
[ 16.100599] usbcore: registered new interface driver ch9200
[ 16.102062] parport0: cannot grant exclusive access for device ks0108
[ 16.102925] ks0108: ERROR: parport didn't register new device
[ 16.247986] panel: panel driver registered on parport0 (io=0x378).
[ 16.252248] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[ 16.253129] ehci-pci: EHCI PCI platform driver
[ 16.253917] ehci-platform: EHCI generic platform driver
[ 16.254894] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 16.255875] ohci-pci: OHCI PCI platform driver
[ 16.256689] ohci-platform: OHCI generic platform driver
[ 16.257770] driver u132_hcd
[ 16.258888] fotg210_hcd: FOTG210 Host Controller (EHCI) Driver
[ 16.259689] Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after
[ 16.261402] usbcore: registered new interface driver cdc_acm
[ 16.262181] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[ 16.263434] usbcore: registered new interface driver cdc_wdm
To reproduce:
# build kernel
cd linux
cp config-5.9.0-rc2-next-20200827-00021-gdc5ef7078b7777 .config
make HOSTCC=gcc-9 CC=gcc-9 ARCH=x86_64 olddefconfig prepare modules_prepare bzImage
git clone https://github.com/intel/lkp-tests.git
cd lkp-tests
bin/lkp qemu -k <bzImage> job-script # job-script is attached in this email
Thanks,
lkp
On 2020-08-28 00:52, Kalle Valo wrote:
> Thomas Pedersen <[email protected]> writes:
>
>> Advertise S1G Capabilities and channels to mac80211.
>>
>> Requires a few fixups to account for missing
>> sband->bitrates, and a custom regulatory db to actually
>> enable the S1G channels.
>>
>> Signed-off-by: Thomas Pedersen <[email protected]>
>> ---
>> drivers/net/wireless/mac80211_hwsim.c | 80
>> ++++++++++++++++++++++++---
>> include/linux/ieee80211.h | 12 +++-
>
> IMHO a mac80211_hwsim patch should not touch ieee80211.h.
Fair enough, I'll split it out.
>> --- a/include/linux/ieee80211.h
>> +++ b/include/linux/ieee80211.h
>> @@ -2385,6 +2385,9 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
>> return spr_len;
>> }
>>
>> +#define SM(f, v) (((v) << f##_SHIFT) & f)
>> +#define MS(f, v) (((v) & f) >> f##_SHIFT)
>> +
>> /* S1G Capabilities Information field */
>> #define S1G_CAPAB_B0_S1G_LONG BIT(0)
>> #define S1G_CAPAB_B0_SGI_1MHZ BIT(1)
>> @@ -2392,9 +2395,16 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
>> #define S1G_CAPAB_B0_SGI_4MHZ BIT(3)
>> #define S1G_CAPAB_B0_SGI_8MHZ BIT(4)
>> #define S1G_CAPAB_B0_SGI_16MHZ BIT(5)
>> -#define S1G_CAPAB_B0_SUPP_CH_WIDTH_MASK (BIT(6) | BIT(7))
>> +#define S1G_CAPAB_B0_SUPP_CH_WIDTH (BIT(6) | BIT(7))
>> #define S1G_CAPAB_B0_SUPP_CH_WIDTH_SHIFT 6
>>
>> +#define S1G_SUPP_CH_WIDTH_2 0
>> +#define S1G_SUPP_CH_WIDTH_4 1
>> +#define S1G_SUPP_CH_WIDTH_8 2
>> +#define S1G_SUPP_CH_WIDTH_16 3
>> +#define S1G_SUPP_CH_WIDTH_MAX(cap) ((1 <<
>> MS(S1G_CAPAB_B0_SUPP_CH_WIDTH, \
>> + cap[0])) << 1)
>
> We have GENMASK(), FIELD_GET() & co nowadays so no need for custom
> macros.
Nice. Thanks for the heads up.
--
thomas