wifi: mac80211: use bandwidth indication element for CSA
authorJohannes Berg <johannes.berg@intel.com>
Wed, 20 Sep 2023 18:25:12 +0000 (21:25 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 25 Sep 2023 07:12:32 +0000 (09:12 +0200)
In CSA, parse the (EHT) bandwidth indication element and
use it (in fact prefer it if present).

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230920211508.43ef01920556.If4f24a61cd634ab1e50eba43899b9e992bf25602@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/linux/ieee80211.h
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/spectmgmt.c
net/mac80211/util.c

index 340d7e0f6bf7924459a535ee28c13ca9af83ed56..f11b7022d9ebc47860c3f171695517fb2d5a928c 100644 (file)
@@ -3139,6 +3139,28 @@ ieee80211_eht_oper_size_ok(const u8 *data, u8 len)
        return len >= needed;
 }
 
+#define IEEE80211_BW_IND_DIS_SUBCH_PRESENT     BIT(1)
+
+struct ieee80211_bandwidth_indication {
+       u8 params;
+       struct ieee80211_eht_operation_info info;
+} __packed;
+
+static inline bool
+ieee80211_bandwidth_indication_size_ok(const u8 *data, u8 len)
+{
+       const struct ieee80211_bandwidth_indication *bwi = (const void *)data;
+
+       if (len < sizeof(*bwi))
+               return false;
+
+       if (bwi->params & IEEE80211_BW_IND_DIS_SUBCH_PRESENT &&
+           len < sizeof(*bwi) + 2)
+               return false;
+
+       return true;
+}
+
 #define LISTEN_INT_USF GENMASK(15, 14)
 #define LISTEN_INT_UI  GENMASK(13, 0)
 
@@ -3596,6 +3618,7 @@ enum ieee80211_eid_ext {
        WLAN_EID_EXT_EHT_OPERATION = 106,
        WLAN_EID_EXT_EHT_MULTI_LINK = 107,
        WLAN_EID_EXT_EHT_CAPABILITY = 108,
+       WLAN_EID_EXT_BANDWIDTH_INDICATION = 135,
 };
 
 /* Action category code */
index d5c5f865323c224a41223863d3e219fdf256d33e..e7856336b5c6c8fece5a9457ce8a24d66fa93274 100644 (file)
@@ -1677,6 +1677,7 @@ struct ieee802_11_elems {
        const struct ieee80211_eht_operation *eht_operation;
        const struct ieee80211_multi_link_elem *ml_basic;
        const struct ieee80211_multi_link_elem *ml_reconf;
+       const struct ieee80211_bandwidth_indication *bandwidth_indication;
 
        /* length of them, respectively */
        u8 ext_capab_len;
@@ -2463,7 +2464,7 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
                                const struct ieee80211_vht_operation *oper,
                                const struct ieee80211_ht_operation *htop,
                                struct cfg80211_chan_def *chandef);
-void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
+void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info,
                                bool support_160, bool support_320,
                                struct cfg80211_chan_def *chandef);
 bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
index e8f16ed235c35beca952e61d7fb8083a64ae5ddb..a211f594f25adeacb02ed0daf9003a1429a785b9 100644 (file)
@@ -109,7 +109,8 @@ ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper,
                return 0;
 
        /* set 160/320 supported to get the full AP definition */
-       ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef);
+       ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
+                                  true, true, &ap_chandef);
        ap_center_freq = ap_chandef.center_freq1;
        ap_bw = 20 * BIT(u8_get_bits(info->control,
                                     IEEE80211_EHT_OPER_CHAN_WIDTH));
@@ -387,7 +388,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
                struct cfg80211_chan_def eht_chandef = *chandef;
 
-               ieee80211_chandef_eht_oper(eht_oper,
+               ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
                                           eht_chandef.width ==
                                           NL80211_CHAN_WIDTH_160,
                                           false, &eht_chandef);
index 871cdac2d0f400b0b8c89e36acacaba9e8cc07da..55959b0b24c514485c9c9536f4d48101c3845435 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2008, Intel Corporation
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2018, 2020, 2022 Intel Corporation
+ * Copyright (C) 2018, 2020, 2022-2023 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -33,12 +33,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_chan_def new_vht_chandef = {};
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
        const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
+       const struct ieee80211_bandwidth_indication *bwi;
        int secondary_channel_offset = -1;
 
        memset(csa_ie, 0, sizeof(*csa_ie));
 
        sec_chan_offs = elems->sec_chan_offs;
        wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+       bwi = elems->bandwidth_indication;
 
        if (conn_flags & (IEEE80211_CONN_DISABLE_HT |
                          IEEE80211_CONN_DISABLE_40MHZ)) {
@@ -132,7 +134,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                break;
        }
 
-       if (wide_bw_chansw_ie) {
+       if (bwi) {
+               /* start with the CSA one */
+               new_vht_chandef = csa_ie->chandef;
+               /* and update the width accordingly */
+               /* FIXME: support 160/320 */
+               ieee80211_chandef_eht_oper(&bwi->info, true, true,
+                                          &new_vht_chandef);
+       } else if (wide_bw_chansw_ie) {
                u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
                struct ieee80211_vht_operation vht_oper = {
                        .chan_width =
index 88f714a75862b32a91da1d63ab2ff58c587679c8..a1e18938ce52b1de9fbedc2bda9cfeb626f212fa 100644 (file)
@@ -990,6 +990,11 @@ ieee80211_parse_extension_element(u32 *crc,
                        }
                }
                break;
+       case WLAN_EID_EXT_BANDWIDTH_INDICATION:
+               if (ieee80211_bandwidth_indication_size_ok(data, len))
+                       elems->bandwidth_indication = data;
+               calc_crc = true;
+               break;
        }
 
        if (crc && calc_crc)
@@ -1005,11 +1010,11 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
        bool calc_crc = params->filter != 0;
        DECLARE_BITMAP(seen_elems, 256);
        u32 crc = params->crc;
-       const u8 *ie;
 
        bitmap_zero(seen_elems, 256);
 
        for_each_element(elem, params->start, params->len) {
+               const struct element *subelem;
                bool elem_parse_failed;
                u8 id = elem->id;
                u8 elen = elem->datalen;
@@ -1267,15 +1272,27 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
                        }
                        /*
                         * This is a bit tricky, but as we only care about
-                        * the wide bandwidth channel switch element, so
-                        * just parse it out manually.
+                        * a few elements, parse them out manually.
                         */
-                       ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
-                                             pos, elen);
-                       if (ie) {
-                               if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie))
+                       subelem = cfg80211_find_elem(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,
+                                                    pos, elen);
+                       if (subelem) {
+                               if (subelem->datalen >= sizeof(*elems->wide_bw_chansw_ie))
                                        elems->wide_bw_chansw_ie =
-                                               (void *)(ie + 2);
+                                               (void *)subelem->data;
+                               else
+                                       elem_parse_failed = true;
+                       }
+
+                       subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION,
+                                                        pos, elen);
+                       if (subelem) {
+                               const void *edata = subelem->data + 1;
+                               u8 edatalen = subelem->datalen - 1;
+
+                               if (ieee80211_bandwidth_indication_size_ok(edata,
+                                                                          edatalen))
+                                       elems->bandwidth_indication = edata;
                                else
                                        elem_parse_failed = true;
                        }
@@ -3746,12 +3763,10 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
        return true;
 }
 
-void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
+void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info,
                                bool support_160, bool support_320,
                                struct cfg80211_chan_def *chandef)
 {
-       struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional;
-
        chandef->center_freq1 =
                ieee80211_channel_to_frequency(info->ccfs0,
                                               chandef->chan->band);
@@ -3920,8 +3935,9 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
                support_320 =
                        eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
 
-               ieee80211_chandef_eht_oper(eht_oper, support_160,
-                                          support_320, &he_chandef);
+               ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
+                                          support_160, support_320,
+                                          &he_chandef);
        }
 
        if (!cfg80211_chandef_valid(&he_chandef)) {