wifi: mac80211: enable EHT mesh support
authorRyder Lee <ryder.lee@mediatek.com>
Mon, 27 Mar 2023 17:07:42 +0000 (01:07 +0800)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 31 Mar 2023 09:09:59 +0000 (11:09 +0200)
Similar to AP beacon, this enables the basic mesh EHT mode, including
EHT operation IE and the fixed field of EHT operation information IE.
As for the optional part (i.e. preamble puncturing bitmap) will be
added in future patch.

Tested-by: Lian Chen <lian.chen@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Link: https://lore.kernel.org/r/1e0ddb9001312451c3e99c4eed2072caf8075f61.1679935259.git.ryder.lee@mediatek.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/util.c

index 8c05835..9b7e184 100644 (file)
@@ -2479,6 +2479,8 @@ void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
                                    enum ieee80211_smps_mode smps_mode,
                                    struct sk_buff *skb);
 u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef);
+u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef,
+                               const struct ieee80211_sta_eht_cap *eht_cap);
 int ieee80211_parse_bitrates(enum nl80211_chan_width width,
                             const struct ieee80211_supported_band *sband,
                             const u8 *srates, int srates_len, u32 *rates);
index 6b94cf2..f723332 100644 (file)
@@ -105,7 +105,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
        ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
                                   ie->vht_operation, ie->ht_operation,
                                   &sta_chan_def);
-       ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, NULL,
+       ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, ie->eht_operation,
                                       &sta_chan_def);
 
        if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
@@ -639,6 +639,65 @@ int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
+int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata,
+                       struct sk_buff *skb, u8 ie_len)
+{
+       const struct ieee80211_sta_he_cap *he_cap;
+       const struct ieee80211_sta_eht_cap *eht_cap;
+       struct ieee80211_supported_band *sband;
+       u8 *pos;
+
+       sband = ieee80211_get_sband(sdata);
+       if (!sband)
+               return -EINVAL;
+
+       he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
+       eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
+       if (!he_cap || !eht_cap ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
+               return 0;
+
+       if (skb_tailroom(skb) < ie_len)
+               return -ENOMEM;
+
+       pos = skb_put(skb, ie_len);
+       ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + ie_len, false);
+
+       return 0;
+}
+
+int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+{
+       const struct ieee80211_sta_eht_cap *eht_cap;
+       struct ieee80211_supported_band *sband;
+       u32 len;
+       u8 *pos;
+
+       sband = ieee80211_get_sband(sdata);
+       if (!sband)
+               return -EINVAL;
+
+       eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
+       if (!eht_cap ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
+               return 0;
+
+       len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
+                     offsetof(struct ieee80211_eht_operation_info, optional);
+
+       if (skb_tailroom(skb) < len)
+               return -ENOMEM;
+
+       pos = skb_put(skb, len);
+       ieee80211_ie_build_eht_oper(pos, &sdata->vif.bss_conf.chandef, eht_cap);
+
+       return 0;
+}
+
 static void ieee80211_mesh_path_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
@@ -697,6 +756,9 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata,
        if (he_oper)
                sdata->vif.bss_conf.he_oper.params =
                        __le32_to_cpu(he_oper->he_oper_params);
+
+       sdata->vif.bss_conf.eht_support =
+               !!ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
 }
 
 bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata,
@@ -903,7 +965,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct mesh_csa_settings *csa;
        enum nl80211_band band;
-       u8 ie_len_he_cap;
+       u8 ie_len_he_cap, ie_len_eht_cap;
        u8 *pos;
        struct ieee80211_sub_if_data *sdata;
        int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon);
@@ -916,6 +978,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 
        ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
                                                NL80211_IFTYPE_MESH_POINT);
+       ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata,
+                                                 NL80211_IFTYPE_MESH_POINT);
        head_len = hdr_len +
                   2 + /* NULL SSID */
                   /* Channel Switch Announcement */
@@ -939,6 +1003,9 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                   2 + 1 + sizeof(struct ieee80211_he_operation) +
                           sizeof(struct ieee80211_he_6ghz_oper) +
                   2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
+                  ie_len_eht_cap +
+                  2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
+                          offsetof(struct ieee80211_eht_operation_info, optional) +
                   ifmsh->ie_len;
 
        bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
@@ -1059,6 +1126,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
            mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
            mesh_add_he_oper_ie(sdata, skb) ||
            mesh_add_he_6ghz_cap_ie(sdata, skb) ||
+           mesh_add_eht_cap_ie(sdata, skb, ie_len_eht_cap) ||
+           mesh_add_eht_oper_ie(sdata, skb) ||
            mesh_add_vendor_ies(sdata, skb))
                goto out_free;
 
index 13f394e..022f412 100644 (file)
@@ -234,6 +234,10 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
                        struct sk_buff *skb);
 int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb);
+int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata,
+                       struct sk_buff *skb, u8 ie_len);
+int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata,
+                        struct sk_buff *skb);
 void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
 int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
 void ieee80211s_init(void);
index ddfe510..8f168bc 100644 (file)
@@ -219,12 +219,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
        bool include_plid = false;
        u16 peering_proto = 0;
        u8 *pos, ie_len = 4;
-       u8 ie_len_he_cap;
+       u8 ie_len_he_cap, ie_len_eht_cap;
        int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot);
        int err = -ENOMEM;
 
        ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
                                                NL80211_IFTYPE_MESH_POINT);
+       ie_len_eht_cap = ieee80211_ie_len_eht_cap(sdata,
+                                                 NL80211_IFTYPE_MESH_POINT);
        skb = dev_alloc_skb(local->tx_headroom +
                            hdr_len +
                            2 + /* capability info */
@@ -241,6 +243,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                            2 + 1 + sizeof(struct ieee80211_he_operation) +
                                    sizeof(struct ieee80211_he_6ghz_oper) +
                            2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
+                           ie_len_eht_cap +
+                           2 + 1 + offsetof(struct ieee80211_eht_operation, optional) +
+                                   offsetof(struct ieee80211_eht_operation_info, optional) +
                            2 + 8 + /* peering IE */
                            sdata->u.mesh.ie_len);
        if (!skb)
@@ -332,7 +337,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                    mesh_add_vht_oper_ie(sdata, skb) ||
                    mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
                    mesh_add_he_oper_ie(sdata, skb) ||
-                   mesh_add_he_6ghz_cap_ie(sdata, skb))
+                   mesh_add_he_6ghz_cap_ie(sdata, skb) ||
+                   mesh_add_eht_cap_ie(sdata, skb, ie_len_eht_cap) ||
+                   mesh_add_eht_oper_ie(sdata, skb))
                        goto free;
        }
 
@@ -451,6 +458,11 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
                                          elems->he_6ghz_capa,
                                          &sta->deflink);
 
+       ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, elems->he_cap,
+                                           elems->he_cap_len,
+                                           elems->eht_cap, elems->eht_cap_len,
+                                           &sta->deflink);
+
        if (bw != sta->sta.deflink.bandwidth)
                changed |= IEEE80211_RC_BW_CHANGED;
 
index 9f5cc16..1527d6a 100644 (file)
@@ -3485,6 +3485,77 @@ out:
        return pos;
 }
 
+u8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef,
+                               const struct ieee80211_sta_eht_cap *eht_cap)
+
+{
+       const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
+                                       &eht_cap->eht_mcs_nss_supp.only_20mhz;
+       struct ieee80211_eht_operation *eht_oper;
+       struct ieee80211_eht_operation_info *eht_oper_info;
+       u8 eht_oper_len = offsetof(struct ieee80211_eht_operation, optional);
+       u8 eht_oper_info_len =
+               offsetof(struct ieee80211_eht_operation_info, optional);
+       u8 chan_width = 0;
+
+       *pos++ = WLAN_EID_EXTENSION;
+       *pos++ = 1 + eht_oper_len + eht_oper_info_len;
+       *pos++ = WLAN_EID_EXT_EHT_OPERATION;
+
+       eht_oper = (struct ieee80211_eht_operation *)pos;
+
+       memcpy(&eht_oper->basic_mcs_nss, eht_mcs_nss, sizeof(*eht_mcs_nss));
+       eht_oper->params |= IEEE80211_EHT_OPER_INFO_PRESENT;
+       pos += eht_oper_len;
+
+       eht_oper_info =
+               (struct ieee80211_eht_operation_info *)eht_oper->optional;
+
+       eht_oper_info->ccfs0 =
+               ieee80211_frequency_to_channel(chandef->center_freq1);
+       if (chandef->center_freq2)
+               eht_oper_info->ccfs1 =
+                       ieee80211_frequency_to_channel(chandef->center_freq2);
+       else
+               eht_oper_info->ccfs1 = 0;
+
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_320:
+               chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ;
+               eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
+               if (chandef->chan->center_freq < chandef->center_freq1)
+                       eht_oper_info->ccfs0 -= 16;
+               else
+                       eht_oper_info->ccfs0 += 16;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               eht_oper_info->ccfs1 = eht_oper_info->ccfs0;
+               if (chandef->chan->center_freq < chandef->center_freq1)
+                       eht_oper_info->ccfs0 -= 8;
+               else
+                       eht_oper_info->ccfs0 += 8;
+               fallthrough;
+       case NL80211_CHAN_WIDTH_80P80:
+               chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ;
+               break;
+       default:
+               chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ;
+               break;
+       }
+       eht_oper_info->control = chan_width;
+       pos += eht_oper_info_len;
+
+       /* TODO: eht_oper_info->optional */
+
+       return pos;
+}
+
 bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
                               struct cfg80211_chan_def *chandef)
 {