Merge remote-tracking branch 'wireless/main' into wireless-next
authorJohannes Berg <johannes.berg@intel.com>
Tue, 6 Sep 2022 08:05:29 +0000 (10:05 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 6 Sep 2022 08:05:39 +0000 (10:05 +0200)
Merge wireless/main to get the rx.link fix, which is needed
for further work in this area.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1  2 
drivers/net/wireless/mac80211_hwsim.c
include/linux/ieee80211.h
net/mac80211/mlme.c
net/mac80211/rx.c

index ad9330ea3c96a6f2bf0b2153c067ed31f57dd84e,1f301a5fb396977da7c2225c2bc98332edd38799..ed3b1c84d5472524f21aec1ab7b871a5eacaa8e7
@@@ -652,6 -652,7 +652,6 @@@ struct mac80211_hwsim_data 
        u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
  
        struct mac_address addresses[2];
 -      struct ieee80211_chanctx_conf *chanctx;
        int channels, idx;
        bool use_chanctx;
        bool destroy_on_close;
@@@ -1298,8 -1299,6 +1298,8 @@@ static void mac80211_hwsim_config_mac_n
        struct sk_buff *skb;
        void *msg_head;
  
 +      WARN_ON(!is_valid_ether_addr(addr));
 +
        if (!_portid && !hwsim_virtio_enabled)
                return;
  
@@@ -1562,19 -1561,6 +1562,19 @@@ static void mac80211_hwsim_add_vendor_r
  #endif
  }
  
 +static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
 +                            struct ieee80211_rx_status *rx_status,
 +                            struct sk_buff *skb)
 +{
 +      memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
 +
 +      mac80211_hwsim_add_vendor_rtap(skb);
 +
 +      data->rx_pkts++;
 +      data->rx_bytes += skb->len;
 +      ieee80211_rx_irqsafe(data->hw, skb);
 +}
 +
  static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
                                          struct sk_buff *skb,
                                          struct ieee80211_channel *chan)
  
                rx_status.mactime = now + data2->tsf_offset;
  
 -              memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
 -
 -              mac80211_hwsim_add_vendor_rtap(nskb);
 -
 -              data2->rx_pkts++;
 -              data2->rx_bytes += nskb->len;
 -              ieee80211_rx_irqsafe(data2->hw, nskb);
 +              mac80211_hwsim_rx(data2, &rx_status, nskb);
        }
        spin_unlock(&hwsim_radio_lock);
  
@@@ -1722,7 -1714,12 +1722,7 @@@ mac80211_hwsim_select_tx_link(struct ma
        if (!vif->valid_links)
                return &vif->bss_conf;
  
 -      /* FIXME: handle multicast TX properly */
 -      if (is_multicast_ether_addr(hdr->addr1) || WARN_ON_ONCE(!sta)) {
 -              unsigned int first_link = ffs(vif->valid_links) - 1;
 -
 -              return rcu_dereference(vif->link_conf[first_link]);
 -      }
 +      WARN_ON(is_multicast_ether_addr(hdr->addr1));
  
        if (WARN_ON_ONCE(!sta->valid_links))
                return &vif->bss_conf;
@@@ -2869,6 -2866,11 +2869,6 @@@ static int mac80211_hwsim_croc(struct i
  static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw,
                                      struct ieee80211_chanctx_conf *ctx)
  {
 -      struct mac80211_hwsim_data *hwsim = hw->priv;
 -
 -      mutex_lock(&hwsim->mutex);
 -      hwsim->chanctx = ctx;
 -      mutex_unlock(&hwsim->mutex);
        hwsim_set_chanctx_magic(ctx);
        wiphy_dbg(hw->wiphy,
                  "add channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
  static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw,
                                          struct ieee80211_chanctx_conf *ctx)
  {
 -      struct mac80211_hwsim_data *hwsim = hw->priv;
 -
 -      mutex_lock(&hwsim->mutex);
 -      hwsim->chanctx = NULL;
 -      mutex_unlock(&hwsim->mutex);
        wiphy_dbg(hw->wiphy,
                  "remove channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
                  ctx->def.chan->center_freq, ctx->def.width,
@@@ -2892,6 -2899,11 +2892,6 @@@ static void mac80211_hwsim_change_chanc
                                          struct ieee80211_chanctx_conf *ctx,
                                          u32 changed)
  {
 -      struct mac80211_hwsim_data *hwsim = hw->priv;
 -
 -      mutex_lock(&hwsim->mutex);
 -      hwsim->chanctx = ctx;
 -      mutex_unlock(&hwsim->mutex);
        hwsim_check_chanctx_magic(ctx);
        wiphy_dbg(hw->wiphy,
                  "change channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n",
@@@ -2983,15 -2995,10 +2983,15 @@@ static int mac80211_hwsim_change_vif_li
                                           u16 old_links, u16 new_links,
                                           struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
  {
 -      unsigned long rem = old_links & ~new_links ?: BIT(0);
 +      unsigned long rem = old_links & ~new_links;
        unsigned long add = new_links & ~old_links;
        int i;
  
 +      if (!old_links)
 +              rem |= BIT(0);
 +      if (!new_links)
 +              add |= BIT(0);
 +
        for_each_set_bit(i, &rem, IEEE80211_MLD_MAX_NUM_LINKS)
                mac80211_hwsim_config_mac_nl(hw, old[i]->addr, false);
  
@@@ -3014,8 -3021,6 +3014,8 @@@ static int mac80211_hwsim_change_sta_li
                                           struct ieee80211_sta *sta,
                                           u16 old_links, u16 new_links)
  {
 +      hwsim_check_sta_magic(sta);
 +
        return 0;
  }
  
@@@ -3203,112 -3208,8 +3203,112 @@@ out_err
  
  static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
        {
 -              .types_mask = BIT(NL80211_IFTYPE_STATION) |
 -                            BIT(NL80211_IFTYPE_AP),
 +              .types_mask = BIT(NL80211_IFTYPE_STATION),
 +              .he_cap = {
 +                      .has_he = true,
 +                      .he_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                              .mac_cap_info[1] =
 +                                      IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                                      IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
 +                              .mac_cap_info[2] =
 +                                      IEEE80211_HE_MAC_CAP2_BSR |
 +                                      IEEE80211_HE_MAC_CAP2_MU_CASCADING |
 +                                      IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                              .mac_cap_info[3] =
 +                                      IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
 +                                      IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
 +                              .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
 +                              .phy_cap_info[1] =
 +                                      IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
 +                                      IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                                      IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                                      IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
 +                              .phy_cap_info[2] =
 +                                      IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
 +
 +                              /* Leave all the other PHY capability bytes
 +                               * unset, as DCM, beam forming, RU and PPE
 +                               * threshold information are not supported
 +                               */
 +                      },
 +                      .he_mcs_nss_supp = {
 +                              .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .rx_mcs_160 = cpu_to_le16(0xffff),
 +                              .tx_mcs_160 = cpu_to_le16(0xffff),
 +                              .rx_mcs_80p80 = cpu_to_le16(0xffff),
 +                              .tx_mcs_80p80 = cpu_to_le16(0xffff),
 +                      },
 +              },
 +              .eht_cap = {
 +                      .has_eht = true,
 +                      .eht_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
 +                                      IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
 +                                      IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1,
 +                              .phy_cap_info[0] =
 +                                      IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
 +                                      IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
 +                                      IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE,
 +                              .phy_cap_info[3] =
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
 +                              .phy_cap_info[4] =
 +                                      IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI |
 +                                      IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK,
 +                              .phy_cap_info[5] =
 +                                      IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT |
 +                                      IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK |
 +                                      IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK,
 +                              .phy_cap_info[6] =
 +                                      IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK |
 +                                      IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK,
 +                              .phy_cap_info[7] =
 +                                      IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW,
 +                      },
 +
 +                      /* For all MCS and bandwidth, set 8 NSS for both Tx and
 +                       * Rx
 +                       */
 +                      .eht_mcs_nss_supp = {
 +                              /*
 +                               * Since B0, B1, B2 and B3 are not set in
 +                               * the supported channel width set field in the
 +                               * HE PHY capabilities information field the
 +                               * device is a 20MHz only device on 2.4GHz band.
 +                               */
 +                              .only_20mhz = {
 +                                      .rx_tx_mcs7_max_nss = 0x88,
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                      },
 +                      /* PPE threshold information is not supported */
 +              },
 +      },
 +      {
 +              .types_mask = BIT(NL80211_IFTYPE_AP),
                .he_cap = {
                        .has_he = true,
                        .he_cap_elem = {
  
  static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
        {
 -              /* TODO: should we support other types, e.g., P2P?*/
 -              .types_mask = BIT(NL80211_IFTYPE_STATION) |
 -                            BIT(NL80211_IFTYPE_AP),
 +              /* TODO: should we support other types, e.g., P2P? */
 +              .types_mask = BIT(NL80211_IFTYPE_STATION),
 +              .he_cap = {
 +                      .has_he = true,
 +                      .he_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                              .mac_cap_info[1] =
 +                                      IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                                      IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
 +                              .mac_cap_info[2] =
 +                                      IEEE80211_HE_MAC_CAP2_BSR |
 +                                      IEEE80211_HE_MAC_CAP2_MU_CASCADING |
 +                                      IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                              .mac_cap_info[3] =
 +                                      IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
 +                                      IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
 +                              .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
 +                              .phy_cap_info[0] =
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
 +                              .phy_cap_info[1] =
 +                                      IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
 +                                      IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                                      IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                                      IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
 +                              .phy_cap_info[2] =
 +                                      IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
 +
 +                              /* Leave all the other PHY capability bytes
 +                               * unset, as DCM, beam forming, RU and PPE
 +                               * threshold information are not supported
 +                               */
 +                      },
 +                      .he_mcs_nss_supp = {
 +                              .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .rx_mcs_160 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_160 = cpu_to_le16(0xfffa),
 +                              .rx_mcs_80p80 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_80p80 = cpu_to_le16(0xfffa),
 +                      },
 +              },
 +              .eht_cap = {
 +                      .has_eht = true,
 +                      .eht_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
 +                                      IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
 +                                      IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1,
 +                              .phy_cap_info[0] =
 +                                      IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
 +                                      IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
 +                                      IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE |
 +                                      IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK,
 +                              .phy_cap_info[1] =
 +                                      IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK,
 +                              .phy_cap_info[2] =
 +                                      IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK,
 +                              .phy_cap_info[3] =
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
 +                              .phy_cap_info[4] =
 +                                      IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI |
 +                                      IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK,
 +                              .phy_cap_info[5] =
 +                                      IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT |
 +                                      IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK |
 +                                      IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK,
 +                              .phy_cap_info[6] =
 +                                      IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK |
 +                                      IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK,
 +                              .phy_cap_info[7] =
 +                                      IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW |
 +                                      IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ,
 +                      },
 +
 +                      /* For all MCS and bandwidth, set 8 NSS for both Tx and
 +                       * Rx
 +                       */
 +                      .eht_mcs_nss_supp = {
 +                              /*
 +                               * As B1 and B2 are set in the supported
 +                               * channel width set field in the HE PHY
 +                               * capabilities information field include all
 +                               * the following MCS/NSS.
 +                               */
 +                              .bw._80 = {
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                              .bw._160 = {
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                      },
 +                      /* PPE threshold information is not supported */
 +              },
 +      },
 +      {
 +              .types_mask = BIT(NL80211_IFTYPE_AP),
                .he_cap = {
                        .has_he = true,
                        .he_cap_elem = {
  
  static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
        {
 -              /* TODO: should we support other types, e.g., P2P?*/
 -              .types_mask = BIT(NL80211_IFTYPE_STATION) |
 -                            BIT(NL80211_IFTYPE_AP),
 +              /* TODO: should we support other types, e.g., P2P? */
 +              .types_mask = BIT(NL80211_IFTYPE_STATION),
 +              .he_6ghz_capa = {
 +                      .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START |
 +                                          IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP |
 +                                          IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN |
 +                                          IEEE80211_HE_6GHZ_CAP_SM_PS |
 +                                          IEEE80211_HE_6GHZ_CAP_RD_RESPONDER |
 +                                          IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS |
 +                                          IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS),
 +              },
 +              .he_cap = {
 +                      .has_he = true,
 +                      .he_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                              .mac_cap_info[1] =
 +                                      IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                                      IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
 +                              .mac_cap_info[2] =
 +                                      IEEE80211_HE_MAC_CAP2_BSR |
 +                                      IEEE80211_HE_MAC_CAP2_MU_CASCADING |
 +                                      IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                              .mac_cap_info[3] =
 +                                      IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
 +                                      IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
 +                              .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
 +                              .phy_cap_info[0] =
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 +                                      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
 +                              .phy_cap_info[1] =
 +                                      IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
 +                                      IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                                      IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                                      IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
 +                              .phy_cap_info[2] =
 +                                      IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
 +                                      IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
 +
 +                              /* Leave all the other PHY capability bytes
 +                               * unset, as DCM, beam forming, RU and PPE
 +                               * threshold information are not supported
 +                               */
 +                      },
 +                      .he_mcs_nss_supp = {
 +                              .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                              .rx_mcs_160 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_160 = cpu_to_le16(0xfffa),
 +                              .rx_mcs_80p80 = cpu_to_le16(0xfffa),
 +                              .tx_mcs_80p80 = cpu_to_le16(0xfffa),
 +                      },
 +              },
 +              .eht_cap = {
 +                      .has_eht = true,
 +                      .eht_cap_elem = {
 +                              .mac_cap_info[0] =
 +                                      IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
 +                                      IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
 +                                      IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1,
 +                              .phy_cap_info[0] =
 +                                      IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ |
 +                                      IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
 +                                      IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
 +                                      IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
 +                                      IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE |
 +                                      IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK,
 +                              .phy_cap_info[1] =
 +                                      IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK,
 +                              .phy_cap_info[2] =
 +                                      IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK |
 +                                      IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK,
 +                              .phy_cap_info[3] =
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
 +                                      IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
 +                              .phy_cap_info[4] =
 +                                      IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
 +                                      IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI |
 +                                      IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK,
 +                              .phy_cap_info[5] =
 +                                      IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
 +                                      IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
 +                                      IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT |
 +                                      IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK |
 +                                      IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK,
 +                              .phy_cap_info[6] =
 +                                      IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK |
 +                                      IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK |
 +                                      IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP,
 +                              .phy_cap_info[7] =
 +                                      IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW |
 +                                      IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
 +                                      IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ,
 +                      },
 +
 +                      /* For all MCS and bandwidth, set 8 NSS for both Tx and
 +                       * Rx
 +                       */
 +                      .eht_mcs_nss_supp = {
 +                              /*
 +                               * As B1 and B2 are set in the supported
 +                               * channel width set field in the HE PHY
 +                               * capabilities information field and 320MHz in
 +                               * 6GHz is supported include all the following
 +                               * MCS/NSS.
 +                               */
 +                              .bw._80 = {
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                              .bw._160 = {
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                              .bw._320 = {
 +                                      .rx_tx_mcs9_max_nss = 0x88,
 +                                      .rx_tx_mcs11_max_nss = 0x88,
 +                                      .rx_tx_mcs13_max_nss = 0x88,
 +                              },
 +                      },
 +                      /* PPE threshold information is not supported */
 +              },
 +      },
 +      {
 +              .types_mask = BIT(NL80211_IFTYPE_AP),
                .he_6ghz_capa = {
                        .capa = cpu_to_le16(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START |
                                            IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP |
@@@ -4262,6 -3896,7 +4262,6 @@@ static int mac80211_hwsim_new_radio(str
                hw->wiphy->max_remain_on_channel_duration = 1000;
                data->if_combination.radar_detect_widths = 0;
                data->if_combination.num_different_channels = data->channels;
 -              data->chanctx = NULL;
        } else {
                data->if_combination.num_different_channels = 1;
                data->if_combination.radar_detect_widths =
@@@ -4836,9 -4471,13 +4836,9 @@@ static int hwsim_cloned_frame_received_
        if (data2->use_chanctx) {
                if (data2->tmp_chan)
                        channel = data2->tmp_chan;
 -              else if (data2->chanctx)
 -                      channel = data2->chanctx->def.chan;
        } else {
                channel = data2->channel;
        }
 -      if (!channel)
 -              goto out;
  
        if (!hwsim_virtio_enabled) {
                if (hwsim_net_get_netgroup(genl_info_net(info)) !=
                                                          rx_status.freq);
                if (!iter_data.channel)
                        goto out;
 +              rx_status.band = iter_data.channel->band;
  
                mutex_lock(&data2->mutex);
                if (!hwsim_chans_compat(iter_data.channel, channel)) {
                        }
                }
                mutex_unlock(&data2->mutex);
 +      } else if (!channel) {
 +              goto out;
        } else {
                rx_status.freq = channel->center_freq;
 +              rx_status.band = channel->band;
        }
  
 -      rx_status.band = channel->band;
        rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
        rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
  
            ieee80211_is_probe_resp(hdr->frame_control))
                rx_status.boottime_ns = ktime_get_boottime_ns();
  
 -      memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
 -      data2->rx_pkts++;
 -      data2->rx_bytes += skb->len;
 -      ieee80211_rx_irqsafe(data2->hw, skb);
 +      mac80211_hwsim_rx(data2, &rx_status, skb);
  
        return 0;
  err:
@@@ -5273,7 -4912,6 +5273,7 @@@ static struct genl_family hwsim_genl_fa
        .module = THIS_MODULE,
        .small_ops = hwsim_ops,
        .n_small_ops = ARRAY_SIZE(hwsim_ops),
 +      .resv_start_op = HWSIM_CMD_DEL_MAC_ADDR + 1,
        .mcgrps = hwsim_mcgrps,
        .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
  };
@@@ -5422,6 -5060,10 +5422,10 @@@ static int hwsim_virtio_handle_cmd(stru
  
        nlh = nlmsg_hdr(skb);
        gnlh = nlmsg_data(nlh);
+       if (skb->len < nlh->nlmsg_len)
+               return -EINVAL;
        err = genlmsg_parse(nlh, &hwsim_genl_family, tb, HWSIM_ATTR_MAX,
                            hwsim_genl_policy, NULL);
        if (err) {
@@@ -5464,7 -5106,8 +5468,8 @@@ static void hwsim_virtio_rx_work(struc
        spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
  
        skb->data = skb->head;
-       skb_set_tail_pointer(skb, len);
+       skb_reset_tail_pointer(skb);
+       skb_put(skb, len);
        hwsim_virtio_handle_cmd(skb);
  
        spin_lock_irqsave(&hwsim_virtio_lock, flags);
index 6f70394417ac92e801fecdd4ff92785cb998b1c3,b6e6d5b407747196890e7a6bd84df135a62218f4..79690938d9a2d81b23a3116d9ae7ca8f945892f1
@@@ -310,9 -310,11 +310,11 @@@ static inline u16 ieee80211_sn_sub(u16 
  struct ieee80211_hdr {
        __le16 frame_control;
        __le16 duration_id;
-       u8 addr1[ETH_ALEN];
-       u8 addr2[ETH_ALEN];
-       u8 addr3[ETH_ALEN];
+       struct_group(addrs,
+               u8 addr1[ETH_ALEN];
+               u8 addr2[ETH_ALEN];
+               u8 addr3[ETH_ALEN];
+       );
        __le16 seq_ctrl;
        u8 addr4[ETH_ALEN];
  } __packed __aligned(2);
@@@ -2886,8 -2888,7 +2888,8 @@@ ieee80211_he_spr_size(const u8 *he_spr_
  /* Calculate 802.11be EHT capabilities IE Tx/Rx EHT MCS NSS Support Field size */
  static inline u8
  ieee80211_eht_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap,
 -                         const struct ieee80211_eht_cap_elem_fixed *eht_cap)
 +                         const struct ieee80211_eht_cap_elem_fixed *eht_cap,
 +                         bool from_ap)
  {
        u8 count = 0;
  
        if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
                count += 3;
  
 -      return count ? count : 4;
 +      if (count)
 +              return count;
 +
 +      return from_ap ? 3 : 4;
  }
  
  /* 802.11be EHT PPE Thresholds */
@@@ -2947,8 -2945,7 +2949,8 @@@ ieee80211_eht_ppe_size(u16 ppe_thres_hd
  }
  
  static inline bool
 -ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len)
 +ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len,
 +                         bool from_ap)
  {
        const struct ieee80211_eht_cap_elem_fixed *elem = (const void *)data;
        u8 needed = sizeof(struct ieee80211_eht_cap_elem_fixed);
                return false;
  
        needed += ieee80211_eht_mcs_nss_size((const void *)he_capa,
 -                                           (const void *)data);
 +                                           (const void *)data,
 +                                           from_ap);
        if (len < needed)
                return false;
  
diff --combined net/mac80211/mlme.c
index 84a3e08a7e848efbb1ecfd362fb3a74929c02ab4,5265d2b6db12def625872fc83357882987721f5f..699e409ef45a3ba5cf1503e8ce3b9f59d1ed175b
@@@ -314,7 -314,7 +314,7 @@@ ieee80211_determine_chantype(struct iee
        if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
                struct cfg80211_chan_def eht_chandef = *chandef;
  
 -              ieee80211_chandef_eht_oper(sdata, eht_oper,
 +              ieee80211_chandef_eht_oper(eht_oper,
                                           eht_chandef.width ==
                                           NL80211_CHAN_WIDTH_160,
                                           false, &eht_chandef);
@@@ -695,7 -695,6 +695,7 @@@ static bool ieee80211_add_vht_ie(struc
  static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb,
                                struct ieee80211_supported_band *sband,
 +                              enum ieee80211_smps_mode smps_mode,
                                ieee80211_conn_flags_t conn_flags)
  {
        u8 *pos, *pre_he_pos;
        /* trim excess if any */
        skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos));
  
 -      ieee80211_ie_build_he_6ghz_cap(sdata, skb);
 +      ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb);
  }
  
  static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata,
        eht_cap_size =
                2 + 1 + sizeof(eht_cap->eht_cap_elem) +
                ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
 -                                         &eht_cap->eht_cap_elem) +
 +                                         &eht_cap->eht_cap_elem,
 +                                         false) +
                ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
                                       eht_cap->eht_cap_elem.phy_cap_info);
        pos = skb_put(skb, eht_cap_size);
 -      ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size);
 +      ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size,
 +                                 false);
  }
  
  static void ieee80211_assoc_add_rates(struct sk_buff *skb,
@@@ -1101,7 -1098,7 +1101,7 @@@ static size_t ieee80211_assoc_link_elem
                                               offset);
  
        if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) {
 -              ieee80211_add_he_ie(sdata, skb, sband,
 +              ieee80211_add_he_ie(sdata, skb, sband, smps_mode,
                                    assoc_data->link[link_id].conn_flags);
                ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY);
        }
@@@ -1223,21 -1220,14 +1223,21 @@@ static void ieee80211_assoc_add_ml_elem
        ml_elem = skb_put(skb, sizeof(*ml_elem));
        ml_elem->control =
                cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC |
 -                          IEEE80211_MLC_BASIC_PRES_EML_CAPA |
                            IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP);
        common = skb_put(skb, sizeof(*common));
        common->len = sizeof(*common) +
 -                    2 + /* EML capabilities */
                      2;  /* MLD capa/ops */
        memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN);
 -      skb_put_data(skb, &eml_capa, sizeof(eml_capa));
 +
 +      /* add EML_CAPA only if needed, see Draft P802.11be_D2.1, 35.3.17 */
 +      if (eml_capa &
 +          cpu_to_le16((IEEE80211_EML_CAP_EMLSR_SUPP |
 +                       IEEE80211_EML_CAP_EMLMR_SUPPORT))) {
 +              common->len += 2; /* EML capabilities */
 +              ml_elem->control |=
 +                      cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA);
 +              skb_put_data(skb, &eml_capa, sizeof(eml_capa));
 +      }
        /* need indication from userspace to support this */
        mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP);
        skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
@@@ -1912,7 -1902,7 +1912,7 @@@ ieee80211_sta_process_chanswitch(struc
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
  
 -      cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
 +      cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 0,
                                          csa_ie.count, csa_ie.mode);
  
        if (local->ops->channel_switch) {
@@@ -2445,29 -2435,6 +2445,29 @@@ static void ieee80211_sta_handle_tspec_
        ieee80211_sta_handle_tspec_ac_params(sdata);
  }
  
 +void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link)
 +{
 +      struct ieee80211_sub_if_data *sdata = link->sdata;
 +      struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_tx_queue_params *params = link->tx_conf;
 +      u8 ac;
 +
 +      for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 +              mlme_dbg(sdata,
 +                       "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
 +                       ac, params[ac].acm,
 +                       params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
 +                       params[ac].txop, params[ac].uapsd,
 +                       ifmgd->tx_tspec[ac].downgraded);
 +              if (!ifmgd->tx_tspec[ac].downgraded &&
 +                  drv_conf_tx(local, link, ac, &params[ac]))
 +                      link_err(link,
 +                               "failed to set TX queue parameters for AC %d\n",
 +                               ac);
 +      }
 +}
 +
  /* MLME */
  static bool
  ieee80211_sta_wmm_params(struct ieee80211_local *local,
                }
        }
  
 -      for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 -              mlme_dbg(sdata,
 -                       "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
 -                       ac, params[ac].acm,
 -                       params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
 -                       params[ac].txop, params[ac].uapsd,
 -                       ifmgd->tx_tspec[ac].downgraded);
 +      for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
                link->tx_conf[ac] = params[ac];
 -              if (!ifmgd->tx_tspec[ac].downgraded &&
 -                  drv_conf_tx(local, link, ac, &params[ac]))
 -                      link_err(link,
 -                               "failed to set TX queue parameters for AC %d\n",
 -                               ac);
 -      }
 +
 +      ieee80211_mgd_set_link_qos_params(link);
  
        /* enable WMM or activate new settings */
        link->conf->qos = true;
@@@ -3443,11 -3420,11 +3443,11 @@@ static void ieee80211_destroy_auth_data
                ieee80211_link_info_change_notify(sdata, &sdata->deflink,
                                                  BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
                mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
-               mutex_unlock(&sdata->local->mtx);
                ieee80211_vif_set_links(sdata, 0);
+               mutex_unlock(&sdata->local->mtx);
        }
  
        cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
@@@ -3485,10 -3462,6 +3485,6 @@@ static void ieee80211_destroy_assoc_dat
                sdata->u.mgd.flags = 0;
                sdata->vif.bss_conf.mu_mimo_owner = false;
  
-               mutex_lock(&sdata->local->mtx);
-               ieee80211_link_release_channel(&sdata->deflink);
-               mutex_unlock(&sdata->local->mtx);
                if (status != ASSOC_REJECTED) {
                        struct cfg80211_assoc_failure data = {
                                .timeout = status == ASSOC_TIMEOUT,
                        cfg80211_assoc_failure(sdata->dev, &data);
                }
  
+               mutex_lock(&sdata->local->mtx);
+               ieee80211_link_release_channel(&sdata->deflink);
                ieee80211_vif_set_links(sdata, 0);
+               mutex_unlock(&sdata->local->mtx);
        }
  
        kfree(assoc_data);
@@@ -3928,7 -3904,6 +3927,7 @@@ static bool ieee80211_assoc_config_link
                .len = elem_len,
                .bss = cbss,
                .link_id = link == &sdata->deflink ? -1 : link->link_id,
 +              .from_ap = true,
        };
        bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
        bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
@@@ -4597,11 -4572,6 +4596,11 @@@ static int ieee80211_prep_channel(struc
        bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
        bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
        struct ieee80211_bss *bss = (void *)cbss->priv;
 +      struct ieee80211_elems_parse_params parse_params = {
 +              .bss = cbss,
 +              .link_id = -1,
 +              .from_ap = true,
 +      };
        struct ieee802_11_elems *elems;
        const struct cfg80211_bss_ies *ies;
        int ret;
        rcu_read_lock();
  
        ies = rcu_dereference(cbss->ies);
 -      elems = ieee802_11_parse_elems(ies->data, ies->len, false, cbss);
 +      parse_params.start = ies->data;
 +      parse_params.len = ies->len;
 +      elems = ieee802_11_parse_elems_full(&parse_params);
        if (!elems) {
                rcu_read_unlock();
                return -ENOMEM;
@@@ -4968,11 -4936,6 +4967,11 @@@ static void ieee80211_rx_mgmt_assoc_res
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
        u16 capab_info, status_code, aid;
 +      struct ieee80211_elems_parse_params parse_params = {
 +              .bss = NULL,
 +              .link_id = -1,
 +              .from_ap = true,
 +      };
        struct ieee802_11_elems *elems;
        int ac;
        const u8 *elem_start;
                return;
  
        elem_len = len - (elem_start - (u8 *)mgmt);
 -      elems = ieee802_11_parse_elems(elem_start, elem_len, false, NULL);
 +      parse_params.start = elem_start;
 +      parse_params.len = elem_len;
 +      elems = ieee802_11_parse_elems_full(&parse_params);
        if (!elems)
                goto notify_driver;
  
        resp.req_ies = ifmgd->assoc_req_ies;
        resp.req_ies_len = ifmgd->assoc_req_ies_len;
        if (sdata->vif.valid_links)
 -              resp.ap_mld_addr = assoc_data->ap_addr;
 +              resp.ap_mld_addr = sdata->vif.cfg.ap_addr;
        cfg80211_rx_assoc_resp(sdata->dev, &resp);
  notify_driver:
        drv_mgd_complete_tx(sdata->local, sdata, &info);
@@@ -5394,10 -5355,6 +5393,10 @@@ static void ieee80211_rx_mgmt_beacon(st
        u32 ncrc = 0;
        u8 *bssid, *variable = mgmt->u.beacon.variable;
        u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
 +      struct ieee80211_elems_parse_params parse_params = {
 +              .link_id = -1,
 +              .from_ap = true,
 +      };
  
        sdata_assert_lock(sdata);
  
        if (baselen > len)
                return;
  
 +      parse_params.start = variable;
 +      parse_params.len = len - baselen;
 +
        rcu_read_lock();
        chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
        if (!chanctx_conf) {
        if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
            !WARN_ON(sdata->vif.valid_links) &&
            ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->link[0].bss)) {
 -              elems = ieee802_11_parse_elems(variable, len - baselen, false,
 -                                             ifmgd->assoc_data->link[0].bss);
 +              parse_params.bss = ifmgd->assoc_data->link[0].bss;
 +              elems = ieee802_11_parse_elems_full(&parse_params);
                if (!elems)
                        return;
  
         */
        if (!ieee80211_is_s1g_beacon(hdr->frame_control))
                ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
 -      elems = ieee802_11_parse_elems_crc(variable, len - baselen,
 -                                         false, care_about_ies, ncrc,
 -                                         link->u.mgd.bss);
 +      parse_params.bss = link->u.mgd.bss;
 +      parse_params.filter = care_about_ies;
 +      parse_params.crc = ncrc;
 +      elems = ieee802_11_parse_elems_full(&parse_params);
        if (!elems)
                return;
        ncrc = elems->crc;
@@@ -5717,13 -5670,6 +5716,13 @@@ void ieee80211_sta_rx_queued_mgmt(struc
  
        sdata_lock(sdata);
  
 +      if (rx_status->link_valid) {
 +              link = sdata_dereference(sdata->link[rx_status->link_id],
 +                                       sdata);
 +              if (!link)
 +                      goto out;
 +      }
 +
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_BEACON:
                ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
                }
                break;
        }
 +out:
        sdata_unlock(sdata);
  }
  
@@@ -6336,8 -6281,6 +6335,8 @@@ void ieee80211_mgd_setup_link(struct ie
        if (sdata->u.mgd.assoc_data)
                ether_addr_copy(link->conf->addr,
                                sdata->u.mgd.assoc_data->link[link_id].addr);
 +      else if (!is_valid_ether_addr(link->conf->addr))
 +              eth_random_addr(link->conf->addr);
  }
  
  /* scan finished notification */
@@@ -6425,6 -6368,9 +6424,6 @@@ static int ieee80211_prep_connection(st
                goto out_err;
        }
  
 -      if (mlo && !is_valid_ether_addr(link->conf->addr))
 -              eth_random_addr(link->conf->addr);
 -
        if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) {
                err = -EINVAL;
                goto out_err;
        return 0;
  
  out_err:
+       ieee80211_link_release_channel(&sdata->deflink);
        ieee80211_vif_set_links(sdata, 0);
        return err;
  }
@@@ -6906,10 -6853,6 +6906,10 @@@ int ieee80211_mgd_assoc(struct ieee8021
                }
        }
  
 +      /* FIXME: no support for 4-addr MLO yet */
 +      if (sdata->u.mgd.use_4addr && req->link_id >= 0)
 +              return -EOPNOTSUPP;
 +
        assoc_data = kzalloc(size, GFP_KERNEL);
        if (!assoc_data)
                return -ENOMEM;
diff --combined net/mac80211/rx.c
index cc139fe5fb7864bfe90b1b69c749dee68cdfac25,45d7e71661e3f0d2f69dcc07ec43fd8980f11870..511c809e2c6b898365698c94bb044946ad0c4381
@@@ -215,19 -215,9 +215,19 @@@ ieee80211_rx_radiotap_hdrlen(struct iee
  }
  
  static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
 +                                         int link_id,
                                           struct sta_info *sta,
                                           struct sk_buff *skb)
  {
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 +
 +      if (link_id >= 0) {
 +              status->link_valid = 1;
 +              status->link_id = link_id;
 +      } else {
 +              status->link_valid = 0;
 +      }
 +
        skb_queue_tail(&sdata->skb_queue, skb);
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
        if (sta)
  }
  
  static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
 +                                       int link_id,
                                         struct sta_info *sta,
                                         struct sk_buff *skb)
  {
        skb->protocol = 0;
 -      __ieee80211_queue_skb_to_iface(sdata, sta, skb);
 +      __ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);
  }
  
  static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
        if (!skb)
                return;
  
 -      ieee80211_queue_skb_to_iface(sdata, NULL, skb);
 +      ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);
  }
  
  /*
@@@ -1405,7 -1394,7 +1405,7 @@@ static void ieee80211_rx_reorder_ampdu(
        /* if this mpdu is fragmented - terminate rx aggregation session */
        sc = le16_to_cpu(hdr->seq_ctrl);
        if (sc & IEEE80211_SCTL_FRAG) {
 -              ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
 +              ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb);
                return;
        }
  
@@@ -1865,6 -1854,7 +1865,6 @@@ static struct ieee80211_key 
  ieee80211_rx_get_bigtk(struct ieee80211_rx_data *rx, int idx)
  {
        struct ieee80211_key *key = NULL;
 -      struct ieee80211_sub_if_data *sdata = rx->sdata;
        int idx2;
  
        /* Make sure key gets set if either BIGTK key index is set so that
                        idx2 = idx - 1;
        }
  
 -      if (rx->sta)
 -              key = rcu_dereference(rx->sta->deflink.gtk[idx]);
 +      if (rx->link_sta)
 +              key = rcu_dereference(rx->link_sta->gtk[idx]);
        if (!key)
 -              key = rcu_dereference(sdata->deflink.gtk[idx]);
 -      if (!key && rx->sta)
 -              key = rcu_dereference(rx->sta->deflink.gtk[idx2]);
 +              key = rcu_dereference(rx->link->gtk[idx]);
 +      if (!key && rx->link_sta)
 +              key = rcu_dereference(rx->link_sta->gtk[idx2]);
        if (!key)
 -              key = rcu_dereference(sdata->deflink.gtk[idx2]);
 +              key = rcu_dereference(rx->link->gtk[idx2]);
  
        return key;
  }
@@@ -1996,15 -1986,15 +1996,15 @@@ ieee80211_rx_h_decrypt(struct ieee80211
                if (mmie_keyidx < NUM_DEFAULT_KEYS ||
                    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
                        return RX_DROP_MONITOR; /* unexpected BIP keyidx */
 -              if (rx->sta) {
 +              if (rx->link_sta) {
                        if (ieee80211_is_group_privacy_action(skb) &&
                            test_sta_flag(rx->sta, WLAN_STA_MFP))
                                return RX_DROP_MONITOR;
  
 -                      rx->key = rcu_dereference(rx->sta->deflink.gtk[mmie_keyidx]);
 +                      rx->key = rcu_dereference(rx->link_sta->gtk[mmie_keyidx]);
                }
                if (!rx->key)
 -                      rx->key = rcu_dereference(rx->sdata->deflink.gtk[mmie_keyidx]);
 +                      rx->key = rcu_dereference(rx->link->gtk[mmie_keyidx]);
        } else if (!ieee80211_has_protected(fc)) {
                /*
                 * The frame was not protected, so skip decryption. However, we
                 * have been expected.
                 */
                struct ieee80211_key *key = NULL;
 -              struct ieee80211_sub_if_data *sdata = rx->sdata;
                int i;
  
                if (ieee80211_is_beacon(fc)) {
                        key = ieee80211_rx_get_bigtk(rx, -1);
                } else if (ieee80211_is_mgmt(fc) &&
                           is_multicast_ether_addr(hdr->addr1)) {
 -                      key = rcu_dereference(rx->sdata->deflink.default_mgmt_key);
 +                      key = rcu_dereference(rx->link->default_mgmt_key);
                } else {
 -                      if (rx->sta) {
 +                      if (rx->link_sta) {
                                for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
 -                                      key = rcu_dereference(rx->sta->deflink.gtk[i]);
 +                                      key = rcu_dereference(rx->link_sta->gtk[i]);
                                        if (key)
                                                break;
                                }
                        }
                        if (!key) {
                                for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
 -                                      key = rcu_dereference(sdata->deflink.gtk[i]);
 +                                      key = rcu_dereference(rx->link->gtk[i]);
                                        if (key)
                                                break;
                                }
                        return RX_DROP_UNUSABLE;
  
                /* check per-station GTK first, if multicast packet */
 -              if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
 -                      rx->key = rcu_dereference(rx->sta->deflink.gtk[keyidx]);
 +              if (is_multicast_ether_addr(hdr->addr1) && rx->link_sta)
 +                      rx->key = rcu_dereference(rx->link_sta->gtk[keyidx]);
  
                /* if not found, try default key */
                if (!rx->key) {
                        if (is_multicast_ether_addr(hdr->addr1))
 -                              rx->key = rcu_dereference(rx->sdata->deflink.gtk[keyidx]);
 +                              rx->key = rcu_dereference(rx->link->gtk[keyidx]);
                        if (!rx->key)
                                rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
  
@@@ -3055,8 -3046,7 +3055,8 @@@ ieee80211_rx_h_data(struct ieee80211_rx
                    (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
                     tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
                        rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
 -                      __ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
 +                      __ieee80211_queue_skb_to_iface(sdata, rx->link_id,
 +                                                     rx->sta, rx->skb);
                        return RX_QUEUED;
                }
        }
@@@ -3646,7 -3636,7 +3646,7 @@@ ieee80211_rx_h_action(struct ieee80211_
        return RX_QUEUED;
  
   queue:
 -      ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
 +      ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
        return RX_QUEUED;
  }
  
@@@ -3804,7 -3794,7 +3804,7 @@@ ieee80211_rx_h_ext(struct ieee80211_rx_
                return RX_DROP_MONITOR;
  
        /* for now only beacons are ext, so queue them */
 -      ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
 +      ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
  
        return RX_QUEUED;
  }
@@@ -3861,7 -3851,7 +3861,7 @@@ ieee80211_rx_h_mgmt(struct ieee80211_rx
                return RX_DROP_MONITOR;
        }
  
 -      ieee80211_queue_skb_to_iface(sdata, rx->sta, rx->skb);
 +      ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
  
        return RX_QUEUED;
  }
@@@ -4084,6 -4074,7 +4084,7 @@@ void ieee80211_release_reorder_timeout(
                .link_id = -1,
        };
        struct tid_ampdu_rx *tid_agg_rx;
+       u8 link_id;
  
        tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
        if (!tid_agg_rx)
                };
                drv_event_callback(rx.local, rx.sdata, &event);
        }
+       /* FIXME: statistics won't be right with this */
+       link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
+       rx.link = rcu_dereference(sta->sdata->link[link_id]);
  
        ieee80211_rx_handlers(&rx, &frames);
  }
@@@ -4518,15 -4512,6 +4522,15 @@@ void ieee80211_check_fast_rx_iface(stru
        mutex_unlock(&local->sta_mtx);
  }
  
 +static bool
 +ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
 +{
 +      if (!sta->mlo)
 +              return false;
 +
 +      return !!(sta->valid_links & BIT(link_id));
 +}
 +
  static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
                              struct ieee80211_fast_rx *fast_rx,
                              int orig_len)
        struct ieee80211_sta_rx_stats *stats;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
        struct sta_info *sta = rx->sta;
 +      struct link_sta_info *link_sta;
        struct sk_buff *skb = rx->skb;
        void *sa = skb->data + ETH_ALEN;
        void *da = skb->data;
  
 -      stats = &sta->deflink.rx_stats;
 +      if (rx->link_id >= 0) {
 +              link_sta = rcu_dereference(sta->link[rx->link_id]);
 +              if (WARN_ON_ONCE(!link_sta)) {
 +                      dev_kfree_skb(rx->skb);
 +                      return;
 +              }
 +      } else {
 +              link_sta = &sta->deflink;
 +      }
 +
 +      stats = &link_sta->rx_stats;
        if (fast_rx->uses_rss)
 -              stats = this_cpu_ptr(sta->deflink.pcpu_rx_stats);
 +              stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
  
        /* statistics part of ieee80211_rx_h_sta_process() */
        if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
                stats->last_signal = status->signal;
                if (!fast_rx->uses_rss)
 -                      ewma_signal_add(&sta->deflink.rx_stats_avg.signal,
 +                      ewma_signal_add(&link_sta->rx_stats_avg.signal,
                                        -status->signal);
        }
  
  
                        stats->chain_signal_last[i] = signal;
                        if (!fast_rx->uses_rss)
 -                              ewma_signal_add(&sta->deflink.rx_stats_avg.chain_signal[i],
 +                              ewma_signal_add(&link_sta->rx_stats_avg.chain_signal[i],
                                                -signal);
                }
        }
@@@ -4649,8 -4623,7 +4653,8 @@@ static bool ieee80211_invoke_fast_rx(st
                u8 da[ETH_ALEN];
                u8 sa[ETH_ALEN];
        } addrs __aligned(2);
 -      struct ieee80211_sta_rx_stats *stats = &sta->deflink.rx_stats;
 +      struct link_sta_info *link_sta;
 +      struct ieee80211_sta_rx_stats *stats;
  
        /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
         * to a common data structure; drivers can implement that per queue
        return true;
   drop:
        dev_kfree_skb(skb);
 +
 +      if (rx->link_id >= 0) {
 +              link_sta = rcu_dereference(sta->link[rx->link_id]);
 +              if (!link_sta)
 +                      return true;
 +      } else {
 +              link_sta = &sta->deflink;
 +      }
 +
        if (fast_rx->uses_rss)
 -              stats = this_cpu_ptr(sta->deflink.pcpu_rx_stats);
 +              stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
 +      else
 +              stats = &link_sta->rx_stats;
  
        stats->dropped++;
        return true;
@@@ -4811,17 -4773,7 +4815,17 @@@ static bool ieee80211_prepare_and_rx_ha
                if (!link)
                        return true;
                rx->link = link;
 +
 +              if (rx->sta) {
 +                      rx->link_sta =
 +                              rcu_dereference(rx->sta->link[rx->link_id]);
 +                      if (!rx->link_sta)
 +                              return true;
 +              }
        } else {
 +              if (rx->sta)
 +                      rx->link_sta = &rx->sta->deflink;
 +
                rx->link = &sdata->deflink;
        }
  
@@@ -4879,7 -4831,6 +4883,7 @@@ static void __ieee80211_rx_handle_8023(
                                       struct list_head *list)
  {
        struct ieee80211_local *local = hw_to_local(hw);
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_fast_rx *fast_rx;
        struct ieee80211_rx_data rx;
  
  
        rx.sta = container_of(pubsta, struct sta_info, sta);
        rx.sdata = rx.sta->sdata;
 -      rx.link = &rx.sdata->deflink;
 +
 +      if (status->link_valid &&
 +          !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id))
 +              goto drop;
 +
 +      /*
 +       * TODO: Should the frame be dropped if the right link_id is not
 +       * available? Or may be it is fine in the current form to proceed with
 +       * the frame processing because with frame being in 802.3 format,
 +       * link_id is used only for stats purpose and updating the stats on
 +       * the deflink is fine?
 +       */
 +      if (status->link_valid)
 +              rx.link_id = status->link_id;
 +
 +      if (rx.link_id >= 0) {
 +              struct ieee80211_link_data *link;
 +
 +              link =  rcu_dereference(rx.sdata->link[rx.link_id]);
 +              if (!link)
 +                      goto drop;
 +              rx.link = link;
 +      } else {
 +              rx.link = &rx.sdata->deflink;
 +      }
  
        fast_rx = rcu_dereference(rx.sta->fast_rx);
        if (!fast_rx)
@@@ -4954,19 -4881,7 +4958,19 @@@ static bool ieee80211_rx_for_interface(
                rx->sta = link_sta->sta;
                rx->link_id = link_sta->link_id;
        } else {
 +              struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 +
                rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2);
 +              if (rx->sta) {
 +                      if (status->link_valid &&
 +                          !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta,
 +                                                             status->link_id))
 +                              return false;
 +
 +                      rx->link_id = status->link_valid ? status->link_id : -1;
 +              } else {
 +                      rx->link_id = -1;
 +              }
        }
  
        return ieee80211_prepare_and_rx_handle(rx, skb, consume);
@@@ -4982,7 -4897,6 +4986,7 @@@ static void __ieee80211_rx_handle_packe
                                         struct list_head *list)
  {
        struct ieee80211_local *local = hw_to_local(hw);
 +      struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_hdr *hdr;
        __le16 fc;
  
        if (ieee80211_is_data(fc)) {
                struct sta_info *sta, *prev_sta;
 +              u8 link_id = status->link_id;
  
                if (pubsta) {
                        rx.sta = container_of(pubsta, struct sta_info, sta);
                        rx.sdata = rx.sta->sdata;
 +
 +                      if (status->link_valid &&
 +                          !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id))
 +                              goto out;
 +
 +                      if (status->link_valid)
 +                              rx.link_id = status->link_id;
 +
 +                      /*
 +                       * In MLO connection, fetch the link_id using addr2
 +                       * when the driver does not pass link_id in status.
 +                       * When the address translation is already performed by
 +                       * driver/hw, the valid link_id must be passed in
 +                       * status.
 +                       */
 +
 +                      if (!status->link_valid && pubsta->mlo) {
 +                              struct ieee80211_hdr *hdr = (void *)skb->data;
 +                              struct link_sta_info *link_sta;
 +
 +                              link_sta = link_sta_info_get_bss(rx.sdata,
 +                                                               hdr->addr2);
 +                              if (!link_sta)
 +                                      goto out;
 +
 +                              rx.link_id = link_sta->link_id;
 +                      }
 +
                        if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
                                return;
                        goto out;
                                continue;
                        }
  
 +                      if ((status->link_valid &&
 +                           !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
 +                                                              link_id)) ||
 +                          (!status->link_valid && prev_sta->sta.mlo))
 +                              continue;
 +
 +                      rx.link_id = status->link_valid ? link_id : -1;
                        rx.sta = prev_sta;
                        rx.sdata = prev_sta->sdata;
                        ieee80211_prepare_and_rx_handle(&rx, skb, false);
                }
  
                if (prev_sta) {
 +                      if ((status->link_valid &&
 +                           !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
 +                                                              link_id)) ||
 +                          (!status->link_valid && prev_sta->sta.mlo))
 +                              goto out;
 +
 +                      rx.link_id = status->link_valid ? link_id : -1;
                        rx.sta = prev_sta;
                        rx.sdata = prev_sta->sdata;
  
@@@ -5237,9 -5108,6 +5241,9 @@@ void ieee80211_rx_list(struct ieee80211
                }
        }
  
 +      if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
 +              goto drop;
 +
        status->rx_flags = 0;
  
        kcov_remote_start_common(skb_get_kcov_handle(skb));