2020-08-31 22:55:57

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 00/22] add support for S1G association

This is the initial 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
- testing 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://lists.infradead.org/pipermail/hostap/2020-August/038748.html),
these patches continue to pass the hwsim tests as well as HEAD.


Thomas Pedersen (22):
ieee80211: redefine S1G bits with GENMASK
nl80211: advertise supported channel width in S1G
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/mac80211_hwsim.c | 92 +++++++++--
include/linux/ieee80211.h | 223 +++++++++++++++++---------
include/net/cfg80211.h | 28 ++++
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 | 200 +++++++++++++++++++++++
net/wireless/chan.c | 140 ++++++++++------
net/wireless/mlme.c | 20 +++
net/wireless/nl80211.c | 52 +++++-
net/wireless/reg.c | 70 ++++++--
net/wireless/scan.c | 80 +++++++--
net/wireless/util.c | 32 ++++
22 files changed, 1090 insertions(+), 266 deletions(-)

--
2.20.1


2020-08-31 22:55:58

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 08/22] nl80211: support S1G capabilities

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 d3d85bd9c0aa..de04c7996b27 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2489,6 +2489,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;
@@ -2503,6 +2505,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 4e119c6afa31..70076492ebc9 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2513,6 +2513,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
@@ -2995,6 +3001,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 3bf595c828db..578d99088719 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -685,6 +685,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 */
@@ -9698,6 +9701,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

2020-08-31 22:56:02

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 09/22] mac80211: support S1G STA capabilities

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 6bf879660a93..9363f43887b4 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 c8504ffc71a1..27b2f1ceca69 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4276,6 +4276,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

2020-08-31 22:56:09

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 12/22] mac80211: convert S1G beacon to scan results

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 9363f43887b4..8bbdc27d57c6 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 27b2f1ceca69..e474b2bf227b 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

2020-08-31 22:56:11

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 13/22] cfg80211: handle Association Response from S1G STA

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 bca4bb443291..690d00505649 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 db7333e20dd7..f8655452b81a 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

2020-08-31 22:56:12

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 14/22] mac80211: encode listen interval for S1G

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 | 9 +++++++++
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 11 +++++++----
net/mac80211/util.c | 20 ++++++++++++++++++++
4 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 690d00505649..a63ad4b4f973 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -2446,6 +2446,15 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
#define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ BIT(0)
#define S1G_OPER_CH_WIDTH_OPER GENMASK(4, 1)

+
+#define LISTEN_INT_USF GENMASK(15, 14)
+#define LISTEN_INT_UI GENMASK(13, 0)
+
+#define IEEE80211_MAX_USF FIELD_MAX(LISTEN_INT_USF)
+#define IEEE80211_MAX_UI FIELD_MAX(LISTEN_INT_UI)
+
+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 8bbdc27d57c6..cf78e356b25d 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 e474b2bf227b..57c6f8bd2bdf 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4391,3 +4391,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 */
+ WARN_ON_ONCE(ui > IEEE80211_MAX_UI);
+ listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) |
+ FIELD_PREP(LISTEN_INT_UI, ui);
+
+ return listen_interval;
+}
--
2.20.1

2020-08-31 22:56:13

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 15/22] mac80211: don't calculate duration for S1G

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 d2136007e2eb..bef19616c5f0 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

2020-08-31 22:56:14

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 16/22] mac80211: handle S1G low rates

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 ec148b3e9c41..9ea743afb986 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

2020-08-31 22:56:16

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 17/22] mac80211: avoid rate init for S1G band

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

2020-08-31 22:56:18

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 19/22] mac80211: support S1G association

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 | 21 ++++++++++
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 | 55 ++++++++++++++++++++++++++
7 files changed, 153 insertions(+), 13 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index a63ad4b4f973..1634650a44ce 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
@@ -2854,6 +2873,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 9ea743afb986..bc78edf1c994 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 b4e39e31a985..086e51a78394 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, &params->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 c0963969a465..1f552f374e97 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 9847cfa0e08c..60786e4b9f5f 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 31845e392759..46cff63fea12 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;
}
@@ -3450,6 +3457,42 @@ 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 (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) {
+ 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;
}

@@ -4356,6 +4399,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

2020-08-31 22:56:21

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 22/22] mac80211_hwsim: fix TSF timestamp write to S1G beacon

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 83d8f54ef93e..18dd23a11ddf 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1742,9 +1742,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

2020-08-31 22:57:07

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 20/22] nl80211: include frequency offset in survey info

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 70076492ebc9..dd5591794e48 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4085,6 +4085,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 {
@@ -4100,6 +4101,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 578d99088719..5468c02d0927 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -9224,6 +9224,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

2020-08-31 22:57:07

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 18/22] mac80211: receive and process S1G beacons

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 cf78e356b25d..9847cfa0e08c 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 57c6f8bd2bdf..31845e392759 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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 11/22] cfg80211: parse S1G Operation element for BSS channel

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 b4c85e8f2107..c2dcb9092257 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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 07/22] mac80211: s1g: choose scanning width based on frequency

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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 04/22] nl80211: correctly validate S1G beacon head

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 7da4d84bcc1a..3bf595c828db 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 4a9ff9ef513f..49e7c0cbbf62 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -399,6 +399,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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 10/22] cfg80211: convert S1G beacon to scan results

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 53fba39d4ba6..bca4bb443291 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 84fc8ab16dd2..b4c85e8f2107 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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 02/22] nl80211: advertise supported channel width in S1G

S1G supports 5 channel widths: 1, 2, 4, 8, and 16. One
channel width is allowed per frequency in each operating
class, so it makes more sense to advertise the specific
channel width allowed.

Signed-off-by: Thomas Pedersen <[email protected]>
---
include/net/cfg80211.h | 15 +++++++++++++++
include/uapi/linux/nl80211.h | 15 +++++++++++++++
net/wireless/nl80211.c | 15 +++++++++++++++
3 files changed, 45 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index c9bce9bba511..e4d4fbcb2edc 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -96,6 +96,16 @@ struct wiphy;
* @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
* on this channel.
* @IEEE80211_CHAN_NO_HE: HE operation is not permitted on this channel.
+ * @IEEE80211_CHAN_1MHZ: 1 MHz bandwidth is permitted
+ * on this channel.
+ * @IEEE80211_CHAN_2MHZ: 2 MHz bandwidth is permitted
+ * on this channel.
+ * @IEEE80211_CHAN_4MHZ: 4 MHz bandwidth is permitted
+ * on this channel.
+ * @IEEE80211_CHAN_8MHZ: 8 MHz bandwidth is permitted
+ * on this channel.
+ * @IEEE80211_CHAN_16MHZ: 16 MHz bandwidth is permitted
+ * on this channel.
*
*/
enum ieee80211_channel_flags {
@@ -113,6 +123,11 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_NO_20MHZ = 1<<11,
IEEE80211_CHAN_NO_10MHZ = 1<<12,
IEEE80211_CHAN_NO_HE = 1<<13,
+ IEEE80211_CHAN_1MHZ = 1<<14,
+ IEEE80211_CHAN_2MHZ = 1<<15,
+ IEEE80211_CHAN_4MHZ = 1<<16,
+ IEEE80211_CHAN_8MHZ = 1<<17,
+ IEEE80211_CHAN_16MHZ = 1<<18,
};

#define IEEE80211_CHAN_NO_HT40 \
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 0584e0d349f0..4e119c6afa31 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3737,6 +3737,16 @@ enum nl80211_wmm_rule {
* @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel
* in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_OFFSET: frequency offset in KHz
+ * @NL80211_FREQUENCY_ATTR_1MHZ: 1 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_2MHZ: 2 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_4MHZ: 4 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_8MHZ: 8 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed
+ * on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -3768,6 +3778,11 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_WMM,
NL80211_FREQUENCY_ATTR_NO_HE,
NL80211_FREQUENCY_ATTR_OFFSET,
+ NL80211_FREQUENCY_ATTR_1MHZ,
+ NL80211_FREQUENCY_ATTR_2MHZ,
+ NL80211_FREQUENCY_ATTR_4MHZ,
+ NL80211_FREQUENCY_ATTR_8MHZ,
+ NL80211_FREQUENCY_ATTR_16MHZ,

/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 52a35e788547..7da4d84bcc1a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1010,6 +1010,21 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
if ((chan->flags & IEEE80211_CHAN_NO_HE) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HE))
goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_1MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_1MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_2MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_2MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_4MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_4MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_8MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_8MHZ))
+ goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_16MHZ) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_16MHZ))
+ goto nla_put_failure;
}

if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
--
2.20.1

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 01/22] ieee80211: redefine S1G bits with GENMASK

The S1G capability fields were defined by ORing BITS()
together, and expecting a custom macro to use the _SHIFT
definitions. Use the Linux kernel GENMASK for the
definitions now, and FIELD_{GET,PREP} to access the fields
in the future.

Take the chance to rename eg. S1G_CAPAB_B0 to the more
compact S1G_CAP0.

Signed-off-by: Thomas Pedersen <[email protected]>
---
include/linux/ieee80211.h | 156 +++++++++++++++++++-------------------
1 file changed, 78 insertions(+), 78 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index c47f43e65a2f..53fba39d4ba6 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -2330,84 +2330,84 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
}

/* S1G Capabilities Information field */
-#define S1G_CAPAB_B0_S1G_LONG BIT(0)
-#define S1G_CAPAB_B0_SGI_1MHZ BIT(1)
-#define S1G_CAPAB_B0_SGI_2MHZ BIT(2)
-#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_SHIFT 6
-
-#define S1G_CAPAB_B1_RX_LDPC BIT(0)
-#define S1G_CAPAB_B1_TX_STBC BIT(1)
-#define S1G_CAPAB_B1_RX_STBC BIT(2)
-#define S1G_CAPAB_B1_SU_BFER BIT(3)
-#define S1G_CAPAB_B1_SU_BFEE BIT(4)
-#define S1G_CAPAB_B1_BFEE_STS_MASK (BIT(5) | BIT(6) | BIT(7))
-#define S1G_CAPAB_B1_BFEE_STS_SHIFT 5
-
-#define S1G_CAPAB_B2_SOUNDING_DIMENSIONS_MASK (BIT(0) | BIT(1) | BIT(2))
-#define S1G_CAPAB_B2_SOUNDING_DIMENSIONS_SHIFT 0
-#define S1G_CAPAB_B2_MU_BFER BIT(3)
-#define S1G_CAPAB_B2_MU_BFEE BIT(4)
-#define S1G_CAPAB_B2_PLUS_HTC_VHT BIT(5)
-#define S1G_CAPAB_B2_TRAVELING_PILOT_MASK (BIT(6) | BIT(7))
-#define S1G_CAPAB_B2_TRAVELING_PILOT_SHIFT 6
-
-#define S1G_CAPAB_B3_RD_RESPONDER BIT(0)
-#define S1G_CAPAB_B3_HT_DELAYED_BA BIT(1)
-#define S1G_CAPAB_B3_MAX_MPDU_LEN BIT(2)
-#define S1G_CAPAB_B3_MAX_AMPDU_LEN_EXP_MASK (BIT(3) | BIT(4))
-#define S1G_CAPAB_B3_MAX_AMPDU_LEN_EXP_SHIFT 3
-#define S1G_CAPAB_B3_MIN_MPDU_START_MASK (BIT(5) | BIT(6) | BIT(7))
-#define S1G_CAPAB_B3_MIN_MPDU_START_SHIFT 5
-
-#define S1G_CAPAB_B4_UPLINK_SYNC BIT(0)
-#define S1G_CAPAB_B4_DYNAMIC_AID BIT(1)
-#define S1G_CAPAB_B4_BAT BIT(2)
-#define S1G_CAPAB_B4_TIME_ADE BIT(3)
-#define S1G_CAPAB_B4_NON_TIM BIT(4)
-#define S1G_CAPAB_B4_GROUP_AID BIT(5)
-#define S1G_CAPAB_B4_STA_TYPE_MASK (BIT(6) | BIT(7))
-#define S1G_CAPAB_B4_STA_TYPE_SHIFT 6
-
-#define S1G_CAPAB_B5_CENT_AUTH_CONTROL BIT(0)
-#define S1G_CAPAB_B5_DIST_AUTH_CONTROL BIT(1)
-#define S1G_CAPAB_B5_AMSDU BIT(2)
-#define S1G_CAPAB_B5_AMPDU BIT(3)
-#define S1G_CAPAB_B5_ASYMMETRIC_BA BIT(4)
-#define S1G_CAPAB_B5_FLOW_CONTROL BIT(5)
-#define S1G_CAPAB_B5_SECTORIZED_BEAM_MASK (BIT(6) | BIT(7))
-#define S1G_CAPAB_B5_SECTORIZED_BEAM_SHIFT 6
-
-#define S1G_CAPAB_B6_OBSS_MITIGATION BIT(0)
-#define S1G_CAPAB_B6_FRAGMENT_BA BIT(1)
-#define S1G_CAPAB_B6_NDP_PS_POLL BIT(2)
-#define S1G_CAPAB_B6_RAW_OPERATION BIT(3)
-#define S1G_CAPAB_B6_PAGE_SLICING BIT(4)
-#define S1G_CAPAB_B6_TXOP_SHARING_IMP_ACK BIT(5)
-#define S1G_CAPAB_B6_VHT_LINK_ADAPT_MASK (BIT(6) | BIT(7))
-#define S1G_CAPAB_B6_VHT_LINK_ADAPT_SHIFT 6
-
-#define S1G_CAPAB_B7_TACK_AS_PS_POLL BIT(0)
-#define S1G_CAPAB_B7_DUP_1MHZ BIT(1)
-#define S1G_CAPAB_B7_MCS_NEGOTIATION BIT(2)
-#define S1G_CAPAB_B7_1MHZ_CTL_RESPONSE_PREAMBLE BIT(3)
-#define S1G_CAPAB_B7_NDP_BFING_REPORT_POLL BIT(4)
-#define S1G_CAPAB_B7_UNSOLICITED_DYN_AID BIT(5)
-#define S1G_CAPAB_B7_SECTOR_TRAINING_OPERATION BIT(6)
-#define S1G_CAPAB_B7_TEMP_PS_MODE_SWITCH BIT(7)
-
-#define S1G_CAPAB_B8_TWT_GROUPING BIT(0)
-#define S1G_CAPAB_B8_BDT BIT(1)
-#define S1G_CAPAB_B8_COLOR_MASK (BIT(2) | BIT(3) | BIT(4))
-#define S1G_CAPAB_B8_COLOR_SHIFT 2
-#define S1G_CAPAB_B8_TWT_REQUEST BIT(5)
-#define S1G_CAPAB_B8_TWT_RESPOND BIT(6)
-#define S1G_CAPAB_B8_PV1_FRAME BIT(7)
-
-#define S1G_CAPAB_B9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0)
+#define S1G_CAP0_S1G_LONG BIT(0)
+#define S1G_CAP0_SGI_1MHZ BIT(1)
+#define S1G_CAP0_SGI_2MHZ BIT(2)
+#define S1G_CAP0_SGI_4MHZ BIT(3)
+#define S1G_CAP0_SGI_8MHZ BIT(4)
+#define S1G_CAP0_SGI_16MHZ BIT(5)
+#define S1G_CAP0_SUPP_CH_WIDTH GENMASK(7, 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 << FIELD_GET(S1G_CAP0_SUPP_CH_WIDTH, \
+ cap[0])) << 1)
+
+#define S1G_CAP1_RX_LDPC BIT(0)
+#define S1G_CAP1_TX_STBC BIT(1)
+#define S1G_CAP1_RX_STBC BIT(2)
+#define S1G_CAP1_SU_BFER BIT(3)
+#define S1G_CAP1_SU_BFEE BIT(4)
+#define S1G_CAP1_BFEE_STS GENMASK(7, 5)
+
+#define S1G_CAP2_SOUNDING_DIMENSIONS GENMASK(2, 0)
+#define S1G_CAP2_MU_BFER BIT(3)
+#define S1G_CAP2_MU_BFEE BIT(4)
+#define S1G_CAP2_PLUS_HTC_VHT BIT(5)
+#define S1G_CAP2_TRAVELING_PILOT GENMASK(7, 6)
+
+#define S1G_CAP3_RD_RESPONDER BIT(0)
+#define S1G_CAP3_HT_DELAYED_BA BIT(1)
+#define S1G_CAP3_MAX_MPDU_LEN BIT(2)
+#define S1G_CAP3_MAX_AMPDU_LEN_EXP GENMASK(4, 3)
+#define S1G_CAP3_MIN_MPDU_START GENMASK(7, 5)
+
+#define S1G_CAP4_UPLINK_SYNC BIT(0)
+#define S1G_CAP4_DYNAMIC_AID BIT(1)
+#define S1G_CAP4_BAT BIT(2)
+#define S1G_CAP4_TIME_ADE BIT(3)
+#define S1G_CAP4_NON_TIM BIT(4)
+#define S1G_CAP4_GROUP_AID BIT(5)
+#define S1G_CAP4_STA_TYPE GENMASK(7, 6)
+
+#define S1G_CAP5_CENT_AUTH_CONTROL BIT(0)
+#define S1G_CAP5_DIST_AUTH_CONTROL BIT(1)
+#define S1G_CAP5_AMSDU BIT(2)
+#define S1G_CAP5_AMPDU BIT(3)
+#define S1G_CAP5_ASYMMETRIC_BA BIT(4)
+#define S1G_CAP5_FLOW_CONTROL BIT(5)
+#define S1G_CAP5_SECTORIZED_BEAM GENMASK(7, 6)
+
+#define S1G_CAP6_OBSS_MITIGATION BIT(0)
+#define S1G_CAP6_FRAGMENT_BA BIT(1)
+#define S1G_CAP6_NDP_PS_POLL BIT(2)
+#define S1G_CAP6_RAW_OPERATION BIT(3)
+#define S1G_CAP6_PAGE_SLICING BIT(4)
+#define S1G_CAP6_TXOP_SHARING_IMP_ACK BIT(5)
+#define S1G_CAP6_VHT_LINK_ADAPT GENMASK(7, 6)
+
+#define S1G_CAP7_TACK_AS_PS_POLL BIT(0)
+#define S1G_CAP7_DUP_1MHZ BIT(1)
+#define S1G_CAP7_MCS_NEGOTIATION BIT(2)
+#define S1G_CAP7_1MHZ_CTL_RESPONSE_PREAMBLE BIT(3)
+#define S1G_CAP7_NDP_BFING_REPORT_POLL BIT(4)
+#define S1G_CAP7_UNSOLICITED_DYN_AID BIT(5)
+#define S1G_CAP7_SECTOR_TRAINING_OPERATION BIT(6)
+#define S1G_CAP7_TEMP_PS_MODE_SWITCH BIT(7)
+
+#define S1G_CAP8_TWT_GROUPING BIT(0)
+#define S1G_CAP8_BDT BIT(1)
+#define S1G_CAP8_COLOR GENMASK(4, 2)
+#define S1G_CAP8_TWT_REQUEST BIT(5)
+#define S1G_CAP8_TWT_RESPOND BIT(6)
+#define S1G_CAP8_PV1_FRAME BIT(7)
+
+#define S1G_CAP9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0)
+
+#define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ BIT(0)
+#define S1G_OPER_CH_WIDTH_OPER GENMASK(4, 1)

/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
--
2.20.1

2020-08-31 22:58:04

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 03/22] cfg80211: regulatory: handle S1G channels

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]>

---

v2:
- drop the freq_reg_info() interface changes and move the
check for S1G band inside. Fixes a driver compile
error.
- fix iterating past bws[] in __freq_reg_info() by
setting initial element to 0.
Reported-by: kernel test robot <[email protected]>
---
net/wireless/reg.c | 70 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 58 insertions(+), 12 deletions(-)

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 0ab7808fcec8..be6f54b70ad3 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[] = {0, 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;
@@ -1631,7 +1633,9 @@ __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)
{
- return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
+ u32 min_bw = center_freq < MHZ_TO_KHZ(1000) ? 1 : 20;
+
+ return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(min_bw));
}
EXPORT_SYMBOL(freq_reg_info);

@@ -1659,6 +1663,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 = &reg_rule->freq_range;

@@ -1678,16 +1683,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;
}

--
2.20.1

2020-08-31 22:58:04

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 21/22] mac80211_hwsim: indicate support for S1G

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]>

---
v2:
- split out changes to ieee80211.h in a separate commit
and use GENMASK + FIELD_GET (Kalle)
---
drivers/net/wireless/mac80211_hwsim.c | 79 ++++++++++++++++++++++++---
1 file changed, 72 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index dce3bc9c9f84..83d8f54ef93e 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -377,6 +377,49 @@ 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_CAP0_SGI_1MHZ | S1G_CAP0_SGI_2MHZ,
+ 0,
+ 0,
+ S1G_CAP3_MAX_MPDU_LEN,
+ 0,
+ S1G_CAP5_AMPDU,
+ 0,
+ S1G_CAP7_DUP_1MHZ,
+ S1G_CAP8_TWT_RESPOND | S1G_CAP8_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 +548,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 +944,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 +970,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 +1387,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 +1569,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 +1715,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 +1736,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 +3134,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 +3178,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 +4381,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 };

--
2.20.1

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 06/22] {cfg,mac}80211: get correct default channel width for S1G

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 96e24ee4c7e8..a3bc50e419fd 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -33,6 +33,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

2020-08-31 22:58:03

by Thomas Pedersen

[permalink] [raw]
Subject: [PATCH v2 05/22] nl80211: support setting S1G channels

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 e4d4fbcb2edc..d3d85bd9c0aa 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5293,6 +5293,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 6a6f2f214c10..96e24ee4c7e8 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -141,9 +141,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;
@@ -155,10 +208,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:
@@ -169,6 +218,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)
@@ -264,53 +337,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 49e7c0cbbf62..ac2bb1a80f2b 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

2020-09-05 18:43:16

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 03/22] cfg80211: regulatory: handle S1G channels

On 2020-08-31 13:55, Thomas Pedersen wrote:
> 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]>
>
> ---
>
> v2:
> - drop the freq_reg_info() interface changes and move the
> check for S1G band inside. Fixes a driver compile
> error.
> - fix iterating past bws[] in __freq_reg_info() by
> setting initial element to 0.
> Reported-by: kernel test robot <[email protected]>
> ---
> net/wireless/reg.c | 70 ++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 58 insertions(+), 12 deletions(-)
>
> diff --git a/net/wireless/reg.c b/net/wireless/reg.c
> index 0ab7808fcec8..be6f54b70ad3 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[] = {0, 1, 2, 4, 5, 8, 10, 16, 20};
> + int i = sizeof(bws) / sizeof(u32) - 1;

This could be 'int i = ARRAY_SIZE(bws) - 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;
> @@ -1631,7 +1633,9 @@ __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)
> {
> - return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
> + u32 min_bw = center_freq < MHZ_TO_KHZ(1000) ? 1 : 20;
> +
> + return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(min_bw));
> }
> EXPORT_SYMBOL(freq_reg_info);
>
> @@ -1659,6 +1663,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 = &reg_rule->freq_range;
>
> @@ -1678,16 +1683,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;
> }

--
thomas

2020-09-06 08:54:17

by Chen, Rong A

[permalink] [raw]
Subject: [mac80211_hwsim] 8cafe19852: hwsim.fst_ap_config_default.fail

Greeting,

FYI, we noticed the following commit (built with gcc-9):

commit: 8cafe19852d95d6494cbd8c1ae24dbd621588a91 ("[PATCH v2 21/22] mac80211_hwsim: indicate support for S1G")
url: https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-support-for-S1G-association/20200901-045826
base: https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-next.git master

in testcase: hwsim
version: hwsim-x86_64-6eb6cf0-1_20200619
with following parameters:

group: hwsim-19
ucode: 0x21



on test machine: 8 threads Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz with 16G memory

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):



If you fix the issue, kindly add following tag
Reported-by: kernel test robot <[email protected]>



2020-09-05 17:49:30 ./run-tests.py fst_ap_config_default
DEV: wlan0: 02:00:00:00:00:00
DEV: wlan1: 02:00:00:00:01:00
DEV: wlan2: 02:00:00:00:02:00
APDEV: wlan3
APDEV: wlan4
START fst_ap_config_default 1/1
Test: FST AP configuration default parameters
kill_pid: pidfile logs/current/myhostapd.pid does not exist - try again after a second
kill_pid: pidfile logs/current/myhostapd.pid does not exist - could not kill the process
hostapd didn't start with valid config parameters
Traceback (most recent call last):
File "./run-tests.py", line 531, in main
t(dev, apdev, params)
File "/lkp/benchmarks/hwsim/tests/hwsim/test_fst_config.py", line 406, in test_fst_ap_config_default
raise Exception("hostapd didn't start with valid config parameters")
Exception: hostapd didn't start with valid config parameters
FAIL fst_ap_config_default 1.201026 2020-09-05 17:49:31.626224
passed 0 test case(s)
skipped 0 test case(s)
failed tests: fst_ap_config_default
2020-09-05 17:49:31 ./run-tests.py fst_ap_config_good
DEV: wlan0: 02:00:00:00:00:00
DEV: wlan1: 02:00:00:00:01:00
DEV: wlan2: 02:00:00:00:02:00
APDEV: wlan3
APDEV: wlan4
START fst_ap_config_good 1/1
Test: FST AP configuration good parameters
kill_pid: pidfile logs/current/myhostapd.pid does not exist - try again after a second
kill_pid: pidfile logs/current/myhostapd.pid does not exist - could not kill the process
hostapd didn't start with valid config parameters
Traceback (most recent call last):
File "./run-tests.py", line 531, in main
t(dev, apdev, params)
File "/lkp/benchmarks/hwsim/tests/hwsim/test_fst_config.py", line 400, in test_fst_ap_config_good
raise Exception("hostapd didn't start with valid config parameters")
Exception: hostapd didn't start with valid config parameters
FAIL fst_ap_config_good 1.214574 2020-09-05 17:49:32.968016
passed 0 test case(s)
skipped 0 test case(s)
failed tests: fst_ap_config_good


To reproduce:

git clone https://github.com/intel/lkp-tests.git
cd lkp-tests
bin/lkp install job.yaml # job file is attached in this email
bin/lkp run job.yaml



Thanks,
Rong Chen


Attachments:
(No filename) (2.92 kB)
config-5.9.0-rc1-00605-g8cafe19852d95d (172.89 kB)
job-script (5.59 kB)
kmsg.xz (136.29 kB)
hwsim (55.05 kB)
job.yaml (4.65 kB)
reproduce (4.52 kB)
Download all attachments

2020-09-06 16:05:59

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 00/22] add support for S1G association

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
> 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://lists.infradead.org/pipermail/hostap/2020-August/038748.html),
> these patches continue to pass the hwsim tests as well as HEAD.


That sounds like we could "hack around" it by sending the S1G data
first, and then the 2.4 GHz, so the latter overwrites it on broken
versions?

Not sure it's worth it though, I'd say it depends a bit on what real
hardware plans are?

I mean, if it's only hwsim for now ... who cares? And if it's going to
be special hardware that only does S1G, then also meh, you need newer
versions to support it, big deal.

But if OTOH a commonly used chipset like e.g. ath9k or ath10k will get
S1G support, then that'd be more relevant?

johannes

2020-09-08 18:04:37

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [mac80211_hwsim] 8cafe19852: hwsim.fst_ap_config_default.fail

- LKML

On 2020-09-06 01:49, kernel test robot wrote:
> Greeting,
>
> FYI, we noticed the following commit (built with gcc-9):
>
> commit: 8cafe19852d95d6494cbd8c1ae24dbd621588a91 ("[PATCH v2 21/22]
> mac80211_hwsim: indicate support for S1G")
> url:
> https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-support-for-S1G-association/20200901-045826
> base:
> https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-next.git
> master

This is really cool. Thanks Rong (?).

I'll move the "fix TSF timestamp write to S1G beacon" patch before the
one which actually exposes S1G to hwsim.

--
thomas

2020-09-08 18:33:22

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 00/22] add support for S1G association

On 2020-09-06 09:04, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>>
>> 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://lists.infradead.org/pipermail/hostap/2020-August/038748.html),
>> these patches continue to pass the hwsim tests as well as HEAD.
>
>
> That sounds like we could "hack around" it by sending the S1G data
> first, and then the 2.4 GHz, so the latter overwrites it on broken
> versions?

Yes that could work.

> Not sure it's worth it though, I'd say it depends a bit on what real
> hardware plans are?
>
> I mean, if it's only hwsim for now ... who cares? And if it's going to
> be special hardware that only does S1G, then also meh, you need newer
> versions to support it, big deal.

AFAIK there are no multi-band S1G chips. The initial focus (from WFA)
seems
to be industrial IOT.

> But if OTOH a commonly used chipset like e.g. ath9k or ath10k will get
> S1G support, then that'd be more relevant?

--
thomas

2020-09-08 18:43:17

by Johannes Berg

[permalink] [raw]
Subject: Re: [mac80211_hwsim] 8cafe19852: hwsim.fst_ap_config_default.fail

On Tue, 2020-09-08 at 11:00 -0700, Thomas Pedersen wrote:
> - LKML
>
> On 2020-09-06 01:49, kernel test robot wrote:
> > Greeting,
> >
> > FYI, we noticed the following commit (built with gcc-9):
> >
> > commit: 8cafe19852d95d6494cbd8c1ae24dbd621588a91 ("[PATCH v2 21/22]
> > mac80211_hwsim: indicate support for S1G")
> > url:
> > https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-support-for-S1G-association/20200901-045826
> > base:
> > https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-next.git
> > master
>
> This is really cool. Thanks Rong (?).
>
> I'll move the "fix TSF timestamp write to S1G beacon" patch before the
> one which actually exposes S1G to hwsim.

Yeah, I was thinking we should do that anyway.

If those two small things (ARRAY_SIZE & patch reordering) are the only
changes, I guess I can do that.

Unless you want to look at the reordering of the nl80211 bands, but you
seemed to say it won't really matter.

johannes

2020-09-08 18:56:34

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [mac80211_hwsim] 8cafe19852: hwsim.fst_ap_config_default.fail

On 2020-09-08 11:41, Johannes Berg wrote:
> On Tue, 2020-09-08 at 11:00 -0700, Thomas Pedersen wrote:
>> - LKML
>>
>> On 2020-09-06 01:49, kernel test robot wrote:
>> > Greeting,
>> >
>> > FYI, we noticed the following commit (built with gcc-9):
>> >
>> > commit: 8cafe19852d95d6494cbd8c1ae24dbd621588a91 ("[PATCH v2 21/22]
>> > mac80211_hwsim: indicate support for S1G")
>> > url:
>> > https://github.com/0day-ci/linux/commits/Thomas-Pedersen/add-support-for-S1G-association/20200901-045826
>> > base:
>> > https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-next.git
>> > master
>>
>> This is really cool. Thanks Rong (?).
>>
>> I'll move the "fix TSF timestamp write to S1G beacon" patch before the
>> one which actually exposes S1G to hwsim.
>
> Yeah, I was thinking we should do that anyway.
>
> If those two small things (ARRAY_SIZE & patch reordering) are the only
> changes, I guess I can do that.

I'll send a v3, the patch reorder doesn't apply cleanly.

> Unless you want to look at the reordering of the nl80211 bands, but you
> seemed to say it won't really matter.

Nah :)

--
thomas

2020-09-18 10:39:07

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 06/22] {cfg,mac}80211: get correct default channel width for S1G

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
> +++ b/net/wireless/chan.c
> @@ -33,6 +33,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;
> + }

Hmm. I'm not sure I want to let you get away with this?

It might be ... convenient, but it's also confusing to see something
like

cfg80211_chandef_create(&out, some_channel, NL80211_CHAN_HT40PLUS);

actually create an S1G channel width?

Yes, this is mostly for backward compatibility, but it's also used in
few (21 in the stack) places. Many of those aren't relevant, e.g. in
net/mac80211/ibss.c it would obviously be clearer to handle the new
NL80211_CHAN_WIDTH_* values with e.g. a cfg80211_get_s1g_chandef()
function or so that does this derivation, instead of running into the

default:
/* fall back to 20 MHz for unsupported modes */
cfg80211_chandef_create(&chandef, cbss->channel,
NL80211_CHAN_NO_HT);

code.

IOW, it seems to me that this function should actually instead throw a
warning (and then perhaps configure something sane?), but not be the
default code path.

johannes

2020-09-18 10:41:24

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 07/22] mac80211: s1g: choose scanning width based on frequency

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
> 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;
> + }

nit: double space after 'goto'

but really I came to say that this probably changes then, if you don't
convince me about the stuff in the previous patch review? :)

So I'm leaving this patch also for now - have applied 1-5 so far.

johannes


2020-09-18 10:43:56

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 08/22] nl80211: support S1G capabilities

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
> + NL80211_ATTR_S1G_CAPABILITY,
> + NL80211_ATTR_S1G_CAPABILITY_MASK,
> /* add attributes here, update the policy in nl80211.c */

nit: keep a blank line?

> [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 },

Please use the NLA_POLICY_EXACT_LEN() as in the line above, unless this
really was meant to be a *minimum* length, which I doubt?

johannes

2020-09-18 10:46:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 11/22] cfg80211: parse S1G Operation element for BSS channel

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
> Extract the BSS primary channel from the S1G Operation
> element.

Out of curiosity, do you even need to?

I mean ... you know what channel you received it on, surely?

> @@ -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;
> }

I *am* a bit worried about this though - do you really want to try to
parse DS elements on S1G, or S1G elements on other bands? Seems like
there ought to be a band check here?

johannes

2020-09-18 10:48:14

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 08/22] nl80211: support S1G capabilities

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
> @@ -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

Maybe that would make more sense in ieee80211.h? Renamed, of course?

johannes

2020-09-18 10:50:23

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 12/22] mac80211: convert S1G beacon to scan results

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:

>
> + if (!elems->parse_error && elems->s1g_capab)
> + bss->s1g = true;

It seems odd to set this only if it's in error - why not always set it
if the band is right?

In fact, why set it at all, since you always know the band?

> + 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;

Seems like you ought to have some structs/sizeof or defines for the
sizes?

johannes

2020-09-18 10:52:31

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 14/22] mac80211: encode listen interval for S1G

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:

> +static const int listen_int_usf[] = { 1, 10, 1000, 10000 };

That should probably be in some C file?

OTOH ... maybe it's small enough to duplicate everywhere? could make it
u16 at least :)

> +__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];

But you really only need it here in one place anyway.

johannes

2020-09-18 10:52:50

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 13/22] cfg80211: handle Association Response from S1G STA

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
> + /* 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();

That ... is rather strange?

Why not check bss->channel->band?

johannes

2020-09-18 10:53:20

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 15/22] mac80211: don't calculate duration for S1G

On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
> 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 d2136007e2eb..bef19616c5f0 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;


I'm not even sure I'd leave a TODO there ... just say

/* device is expected to do this */

or so?

johannes

2020-09-18 10:55:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 15/22] mac80211: don't calculate duration for S1G

Oops, sorry. I just realized I've been replying to v2, while *looking*
at v3 in patchwork ...

I'll continue on v3, but this seems mostly the same.

johannes

2020-09-21 04:35:00

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 08/22] nl80211: support S1G capabilities

On 2020-09-18 03:47, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>> @@ -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
>
> Maybe that would make more sense in ieee80211.h? Renamed, of course?

Makes sense, thanks I'll take care of that and the other comments in
this
patch.

--
thomas

2020-09-21 05:00:59

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 06/22] {cfg,mac}80211: get correct default channel width for S1G

On 2020-09-18 03:38, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>>
>> +++ b/net/wireless/chan.c
>> @@ -33,6 +33,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;
>> + }
>
> Hmm. I'm not sure I want to let you get away with this?
>
> It might be ... convenient, but it's also confusing to see something
> like
>
> cfg80211_chandef_create(&out, some_channel, NL80211_CHAN_HT40PLUS);
>
> actually create an S1G channel width?

Yes I agree that looks like it should be an error.

> Yes, this is mostly for backward compatibility, but it's also used in
> few (21 in the stack) places. Many of those aren't relevant, e.g. in
> net/mac80211/ibss.c it would obviously be clearer to handle the new
> NL80211_CHAN_WIDTH_* values with e.g. a cfg80211_get_s1g_chandef()
> function or so that does this derivation, instead of running into the
>
> default:
> /* fall back to 20 MHz for unsupported modes */
> cfg80211_chandef_create(&chandef, cbss->channel,
> NL80211_CHAN_NO_HT);

Yes, but then what would we pass to cfg80211_chandef_create()? We should
probably avoid adding an additional NL80211_CHAN_S1G if enum
nl80211_channel_type is legacy.

It seems like NL80211_CHAN_NO_HT is often used as "give me the default
channel width". See cfg80211_get_chandef_type() when it throws up its
hands
and returns NL80211_CHAN_NO_HT when encountering an unknown
chandef->width.
Also IBSS passes NL80211_CHAN_NO_HT when the BSS is actually 5 or 10MHz.

Maybe (instead of adding a new nl80211_channel_type)
cfg80211_chandef_create() throws a warning if anything but
NL80211_CHAN_NO_HT
is passed with an S1G frequency?

> IOW, it seems to me that this function should actually instead throw a
> warning (and then perhaps configure something sane?), but not be the
> default code path.

Yes, but I'd also like to avoid making the caller worry about the value
of a
parameter which only exists for HT reasons (?).

--
thomas

2020-09-21 05:08:37

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 07/22] mac80211: s1g: choose scanning width based on frequency

On 2020-09-18 03:40, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>> 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;
>> + }
>
> nit: double space after 'goto'
>
> but really I came to say that this probably changes then, if you don't
> convince me about the stuff in the previous patch review? :)
>
> So I'm leaving this patch also for now - have applied 1-5 so far.

Thanks. I'm not really sure what else would make sense here?
scan_req->scan_width is constant across all channels in
scan_req->channels so for S1G we can either filter the scan_req channels
list based on scan_width (kind of strange and unexpected), or deduce the
correct chanenl width for each channel in the list and ignore scan_width
(mostly correct). It seems like scan_width is currently only used for
scanning at 5 or 10MHz anyway?

--
thomas

2020-09-21 05:15:51

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 11/22] cfg80211: parse S1G Operation element for BSS channel

On 2020-09-18 03:45, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>> Extract the BSS primary channel from the S1G Operation
>> element.
>
> Out of curiosity, do you even need to?
>
> I mean ... you know what channel you received it on, surely?

Consider the case where the BSS is operating @ 2Mhz, but primary is one
of
the 1Mhz channels. The hardware (or driver) may not be able to tell you
exactly which primary channel (upper or lower) the packet came in on.

>> @@ -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;
>> }
>
> I *am* a bit worried about this though - do you really want to try to
> parse DS elements on S1G, or S1G elements on other bands? Seems like
> there ought to be a band check here?

OK. I'll rework this to handle garbage input a little better.

--
thomas

2020-09-21 05:49:09

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 12/22] mac80211: convert S1G beacon to scan results

On 2020-09-18 03:48, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
>>
>> + if (!elems->parse_error && elems->s1g_capab)
>> + bss->s1g = true;
>
> It seems odd to set this only if it's in error - why not always set it
> if the band is right?

True.

> In fact, why set it at all, since you always know the band?

It seemed like a convenient shorthand, but looking at
net/mac80211/mlme.c the
band/channel is available (sband->band or cbss->channel->band) wherever
we
check s1g->bss so maybe it's not needed. I'll remove it to keep things
simple.

>> + 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;
>
> Seems like you ought to have some structs/sizeof or defines for the
> sizes?

Yes I'll find something. Thanks.

--
thomas

2020-09-21 05:53:02

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 13/22] cfg80211: handle Association Response from S1G STA

On 2020-09-18 03:50, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>>
>> + /* 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();
>
> That ... is rather strange?
>
> Why not check bss->channel->band?

Thanks! That saves a lot of work :)

--
thomas

2020-09-21 05:57:09

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 14/22] mac80211: encode listen interval for S1G

On 2020-09-18 03:51, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>
>> +static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
>
> That should probably be in some C file?
>
> OTOH ... maybe it's small enough to duplicate everywhere? could make it
> u16 at least :)
>
>> +__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];
>
> But you really only need it here in one place anyway.

OK better to keep it private to that file then.

--
thomas

2020-09-21 06:04:29

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 15/22] mac80211: don't calculate duration for S1G

On 2020-09-18 03:52, Johannes Berg wrote:
> On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
>> 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 d2136007e2eb..bef19616c5f0 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;
>
>
> I'm not even sure I'd leave a TODO there ... just say
>
> /* device is expected to do this */

Eventually it would be nice if mac80211 could calculate duration for
S1G? Do you know why it doesn't for HT/VHT?

--
thomas

2020-09-21 06:54:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 11/22] cfg80211: parse S1G Operation element for BSS channel

On Sun, 2020-09-20 at 22:12 -0700, Thomas Pedersen wrote:
> On 2020-09-18 03:45, Johannes Berg wrote:
> > On Mon, 2020-08-31 at 13:55 -0700, Thomas Pedersen wrote:
> > > Extract the BSS primary channel from the S1G Operation
> > > element.
> >
> > Out of curiosity, do you even need to?
> >
> > I mean ... you know what channel you received it on, surely?
>
> Consider the case where the BSS is operating @ 2Mhz, but primary is one
> of
> the 1Mhz channels. The hardware (or driver) may not be able to tell you
> exactly which primary channel (upper or lower) the packet came in on.

Ah, OK, makes sense. Somehow based on a comment somewhere else I thought
you were saying that the channels are basically all unique in their
(center frequency, bandwidth) tuple, and was assuming you'd actually
have to scan them that way.

johannes

2020-09-21 06:55:07

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 15/22] mac80211: don't calculate duration for S1G

On Sun, 2020-09-20 at 23:03 -0700, Thomas Pedersen wrote:
>
> > I'm not even sure I'd leave a TODO there ... just say
> >
> > /* device is expected to do this */
>
> Eventually it would be nice if mac80211 could calculate duration for
> S1G? Do you know why it doesn't for HT/VHT?

I'm not sure it matters. It almost doesn't even matter for legacy
(CCK/OFDM) because devices practically always do it anyway?

And for HT/VHT it's just quite a bit more complicated, so we never
bothered, since it wasn't necessary.

It couldn't even really be done since there are A-MPDUs and other things
happening at a lower layer.

johannes

2020-09-21 07:00:15

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 07/22] mac80211: s1g: choose scanning width based on frequency

On Sun, 2020-09-20 at 22:06 -0700, Thomas Pedersen wrote:
>
> > > + /* 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;
> > > + }
> >
> > nit: double space after 'goto'
> >
> > but really I came to say that this probably changes then, if you don't
> > convince me about the stuff in the previous patch review? :)
> >
> > So I'm leaving this patch also for now - have applied 1-5 so far.
>
> Thanks. I'm not really sure what else would make sense here?
> scan_req->scan_width is constant across all channels in
> scan_req->channels so for S1G we can either filter the scan_req channels
> list based on scan_width (kind of strange and unexpected), or deduce the
> correct chanenl width for each channel in the list and ignore scan_width
> (mostly correct). It seems like scan_width is currently only used for
> scanning at 5 or 10MHz anyway?

Yeah, that's true, it's sort of undefined if you're not in 5 or 10, and
then we currently assume it's 20, but obviously for S1G we should assume
then it's 1 MHz or something.

FWIW, here's probably where I thought you have a unique (freq, bw) tuple
and thus shouldn't really need to have the parsing in the other patch?
But I never looked at the spec so far ...


Anyway, wrt. the code here, I think perhaps you should just simply
remove the reference to scan_width? I'm not sure what you'd really do
with it, since it's a 5/10 MHz thing?

TBH though, offhand I don't even know how the 5/10 MHz scanning is
supposed to work?

johannes

2020-09-21 07:02:25

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v2 06/22] {cfg,mac}80211: get correct default channel width for S1G

On Sun, 2020-09-20 at 21:59 -0700, Thomas Pedersen wrote:
>
> > default:
> > /* fall back to 20 MHz for unsupported modes */
> > cfg80211_chandef_create(&chandef, cbss->channel,
> > NL80211_CHAN_NO_HT);
>
> Yes, but then what would we pass to cfg80211_chandef_create()?

I'd say we just shouldn't call it if there's a chance that it's an S1G
channel?

> We should
> probably avoid adding an additional NL80211_CHAN_S1G if enum
> nl80211_channel_type is legacy.

Agree.

> It seems like NL80211_CHAN_NO_HT is often used as "give me the default
> channel width". See cfg80211_get_chandef_type() when it throws up its
> hands
> and returns NL80211_CHAN_NO_HT when encountering an unknown
> chandef->width.
> Also IBSS passes NL80211_CHAN_NO_HT when the BSS is actually 5 or 10MHz.

Yeah, agree it's a bit of a mess, but I'm not really in favour of
keeping that mess :)

Also, that's a WARN_ON() there, so the NL80211_CHAN_NO_HT isn't meant to
be anything *valid* in that case, just a value that doesn't cause
crashes or other bad behaviour further down the road if we hit that
path.

> Maybe (instead of adding a new nl80211_channel_type)
> cfg80211_chandef_create() throws a warning if anything but
> NL80211_CHAN_NO_HT
> is passed with an S1G frequency?

I'd literally just add

cfg80211_chandef_create_s1g()

and just not have the argument at all? Or just fill the chandef
manually, but of course that's a bit tedious sometimes.

> > IOW, it seems to me that this function should actually instead throw a
> > warning (and then perhaps configure something sane?), but not be the
> > default code path.
>
> Yes, but I'd also like to avoid making the caller worry about the value
> of a parameter which only exists for HT reasons (?).

It mostly isn't even for HT reasons ... for HT, we could perfectly well
fill the chandef directly, and do in many cases.

johannes

2020-09-21 16:30:19

by Thomas Pedersen

[permalink] [raw]
Subject: Re: [PATCH v2 06/22] {cfg,mac}80211: get correct default channel width for S1G

On 2020-09-21 00:01, Johannes Berg wrote:
> On Sun, 2020-09-20 at 21:59 -0700, Thomas Pedersen wrote:
>>
>> > default:
>> > /* fall back to 20 MHz for unsupported modes */
>> > cfg80211_chandef_create(&chandef, cbss->channel,
>> > NL80211_CHAN_NO_HT);
>>
>> Yes, but then what would we pass to cfg80211_chandef_create()?
>
> I'd say we just shouldn't call it if there's a chance that it's an S1G
> channel?
>
>> We should
>> probably avoid adding an additional NL80211_CHAN_S1G if enum
>> nl80211_channel_type is legacy.
>
> Agree.
>
>> It seems like NL80211_CHAN_NO_HT is often used as "give me the default
>> channel width". See cfg80211_get_chandef_type() when it throws up its
>> hands
>> and returns NL80211_CHAN_NO_HT when encountering an unknown
>> chandef->width.
>> Also IBSS passes NL80211_CHAN_NO_HT when the BSS is actually 5 or
>> 10MHz.
>
> Yeah, agree it's a bit of a mess, but I'm not really in favour of
> keeping that mess :)
>
> Also, that's a WARN_ON() there, so the NL80211_CHAN_NO_HT isn't meant
> to
> be anything *valid* in that case, just a value that doesn't cause
> crashes or other bad behaviour further down the road if we hit that
> path.
>
>> Maybe (instead of adding a new nl80211_channel_type)
>> cfg80211_chandef_create() throws a warning if anything but
>> NL80211_CHAN_NO_HT
>> is passed with an S1G frequency?
>
> I'd literally just add
>
> cfg80211_chandef_create_s1g()
>
> and just not have the argument at all? Or just fill the chandef
> manually, but of course that's a bit tedious sometimes.

Then the caller has to make the determination which function to call
based on the chan->band, but we've told the function implicitly by
passing chan. It doesn't make sense to me to push that complexity onto
the caller.

That said, it actually looks like cfg80211_chandef_create() is only
called when a chantype is passed to nl80211 (legacy), in DFS, or HT.

After removing this hunk the S1G tests still pass, so how about we just
drop it :)

>> > IOW, it seems to me that this function should actually instead throw a
>> > warning (and then perhaps configure something sane?), but not be the
>> > default code path.
>>
>> Yes, but I'd also like to avoid making the caller worry about the
>> value
>> of a parameter which only exists for HT reasons (?).
>
> It mostly isn't even for HT reasons ... for HT, we could perfectly well
> fill the chandef directly, and do in many cases.
>
> johannes

--
thomas