--- /dev/null
+--- a/ath10k-5.15/mac.c
++++ b/ath10k-5.15/mac.c
+@@ -788,7 +788,7 @@ int ath10k_mac_vif_chan(struct ieee80211
+ struct ieee80211_chanctx_conf *conf;
+
+ rcu_read_lock();
+- conf = rcu_dereference(vif->chanctx_conf);
++ conf = rcu_dereference(vif->bss_conf.chanctx_conf);
+ if (!conf) {
+ rcu_read_unlock();
+ return -ENOENT;
+@@ -1764,8 +1764,8 @@ static int ath10k_vdev_start_restart(str
+ arg.channel.chan_radar =
+ !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
+ } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+- arg.ssid = arvif->vif->bss_conf.ssid;
+- arg.ssid_len = arvif->vif->bss_conf.ssid_len;
++ arg.ssid = arvif->vif->cfg.ssid;
++ arg.ssid_len = arvif->vif->cfg.ssid_len;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+@@ -1890,7 +1890,7 @@ static int ath10k_mac_setup_bcn_tmpl(str
+ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ return 0;
+
+- bcn = ieee80211_beacon_get_template(hw, vif, &offs);
++ bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+ if (!bcn) {
+ ath10k_warn(ar, "failed to get beacon template from mac80211\n");
+ return -EPERM;
+@@ -2083,8 +2083,7 @@ static void ath10k_control_beaconing(str
+ }
+
+ static void ath10k_control_ibss(struct ath10k_vif *arvif,
+- struct ieee80211_bss_conf *info,
+- const u8 self_peer[ETH_ALEN])
++ struct ieee80211_vif *vif)
+ {
+ struct ath10k *ar = arvif->ar;
+ u32 vdev_param;
+@@ -2092,7 +2091,7 @@ static void ath10k_control_ibss(struct a
+
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
+- if (!info->ibss_joined) {
++ if (!vif->cfg.ibss_joined) {
+ if (is_zero_ether_addr(arvif->bssid))
+ return;
+
+@@ -2298,7 +2297,7 @@ static void ath10k_mac_vif_ap_csa_count_
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
+ return;
+
+- if (!vif->csa_active)
++ if (!vif->bss_conf.csa_active)
+ return;
+
+ if (!arvif->is_up)
+@@ -2433,7 +2432,7 @@ static void ath10k_peer_assoc_h_basic(st
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+- aid = vif->bss_conf.aid;
++ aid = vif->cfg.aid;
+ else
+ aid = sta->aid;
+
+@@ -2463,7 +2462,8 @@ static void ath10k_peer_assoc_h_crypto(s
+ return;
+
+ bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid,
+- info->ssid_len ? info->ssid : NULL, info->ssid_len,
++ vif->cfg.ssid_len ? vif->cfg.ssid : NULL,
++ vif->cfg.ssid_len,
+ IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
+ if (bss) {
+ const struct cfg80211_bss_ies *ies;
+@@ -2521,7 +2521,7 @@ static void ath10k_peer_assoc_h_rates(st
+
+ band = def.chan->band;
+ sband = ar->hw->wiphy->bands[band];
+- ratemask = sta->supp_rates[band];
++ ratemask = sta->deflink.supp_rates[band];
+ ratemask &= arvif->bitrate_mask.control[band].legacy;
+ rates = sband->bitrates;
+
+@@ -2770,7 +2770,7 @@ static void ath10k_peer_assoc_h_ht(struc
+ struct ieee80211_sta *sta,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
+- const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
++ const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ struct cfg80211_chan_def def;
+ enum nl80211_band band;
+@@ -2814,7 +2814,7 @@ static void ath10k_peer_assoc_h_ht(struc
+ if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
+ arg->peer_flags |= ar->wmi.peer_flags->ldbc;
+
+- if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
++ if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) {
+ arg->peer_flags |= ar->wmi.peer_flags->bw40;
+ arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
+ }
+@@ -2883,7 +2883,7 @@ static void ath10k_peer_assoc_h_ht(struc
+ arg->peer_ht_rates.rates[i] = i;
+ } else {
+ arg->peer_ht_rates.num_rates = n;
+- arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss);
++ arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss);
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
+@@ -3045,7 +3045,7 @@ static void ath10k_peer_assoc_h_vht(stru
+ struct ieee80211_sta *sta,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
+- const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
++ const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ struct ath10k_hw_params *hw = &ar->hw_params;
+ struct cfg80211_chan_def def;
+@@ -3087,10 +3087,10 @@ static void ath10k_peer_assoc_h_vht(stru
+ (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+ ampdu_factor)) - 1);
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
+ arg->peer_flags |= ar->wmi.peer_flags->bw80;
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_160)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ arg->peer_flags |= ar->wmi.peer_flags->bw160;
+
+ /* Calculate peer NSS capability from VHT capabilities if STA
+@@ -3104,7 +3104,7 @@ static void ath10k_peer_assoc_h_vht(stru
+ vht_mcs_mask[i])
+ max_nss = i + 1;
+ }
+- arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss);
++ arg->peer_num_spatial_streams = min(sta->deflink.rx_nss, max_nss);
+ arg->peer_vht_rates.rx_max_rate =
+ __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
+ arg->peer_vht_rates.rx_mcs_set =
+@@ -3266,7 +3266,7 @@ static bool ath10k_mac_sta_has_ofdm_only
+ {
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ u32 msk = arvif->bitrate_mask.control[NL80211_BAND_2GHZ].legacy &
+- sta->supp_rates[NL80211_BAND_2GHZ];
++ sta->deflink.supp_rates[NL80211_BAND_2GHZ];
+ /* We have 12 bits of legacy rates, first 4 are /b (CCK) rates. */
+ return (msk & 0xff0) && !(msk & 0xf);
+ }
+@@ -3276,7 +3276,7 @@ static bool ath10k_mac_sta_has_ofdm_and_
+ {
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+ u32 msk = arvif->bitrate_mask.control[NL80211_BAND_2GHZ].legacy &
+- sta->supp_rates[NL80211_BAND_2GHZ];
++ sta->deflink.supp_rates[NL80211_BAND_2GHZ];
+ /* We have 12 bits of legacy rates, first 4 are /b (CCK) rates. */
+ return ((msk & 0xf) && (msk & 0xff0));
+ }
+@@ -3284,8 +3284,10 @@ static bool ath10k_mac_sta_has_ofdm_and_
+ static enum wmi_phy_mode ath10k_mac_get_phymode_vht(struct ath10k *ar,
+ struct ieee80211_sta *sta)
+ {
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_160) {
+- switch (sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
++ struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
++
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) {
++ switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ return MODE_11AC_VHT160;
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+@@ -3296,13 +3298,13 @@ static enum wmi_phy_mode ath10k_mac_get_
+ }
+ }
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
+ return MODE_11AC_VHT80;
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
+ return MODE_11AC_VHT40;
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20)
+ return MODE_11AC_VHT20;
+
+ return MODE_UNKNOWN;
+@@ -3329,15 +3331,15 @@ static void ath10k_peer_assoc_h_phymode(
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+- if (sta->vht_cap.vht_supported &&
++ if (sta->deflink.vht_cap.vht_supported &&
+ !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) {
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
+ phymode = MODE_11AC_VHT40;
+ else
+ phymode = MODE_11AC_VHT20;
+- } else if (sta->ht_cap.ht_supported &&
++ } else if (sta->deflink.ht_cap.ht_supported &&
+ !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) {
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40)
+ phymode = MODE_11NG_HT40;
+ else
+ phymode = MODE_11NG_HT20;
+@@ -3354,12 +3356,12 @@ static void ath10k_peer_assoc_h_phymode(
+ /*
+ * Check VHT first.
+ */
+- if (sta->vht_cap.vht_supported &&
++ if (sta->deflink.vht_cap.vht_supported &&
+ !ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) {
+ phymode = ath10k_mac_get_phymode_vht(ar, sta);
+- } else if (sta->ht_cap.ht_supported &&
++ } else if (sta->deflink.ht_cap.ht_supported &&
+ !ath10k_peer_assoc_h_ht_masked(ht_mcs_mask)) {
+- if (sta->bandwidth >= IEEE80211_STA_RX_BW_40)
++ if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40)
+ phymode = MODE_11NA_HT40;
+ else
+ phymode = MODE_11NA_HT20;
+@@ -3373,8 +3375,8 @@ static void ath10k_peer_assoc_h_phymode(
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM phymode %s legacy-supp-rates: 0x%x arvif-legacy-rates: 0x%x vht-supp: %d\n",
+- sta->addr, ath10k_wmi_phymode_str(phymode), sta->supp_rates[band],
+- arvif->bitrate_mask.control[band].legacy, sta->vht_cap.vht_supported);
++ sta->addr, ath10k_wmi_phymode_str(phymode), sta->deflink.supp_rates[band],
++ arvif->bitrate_mask.control[band].legacy, sta->deflink.vht_cap.vht_supported);
+
+ arg->peer_phymode = phymode;
+ WARN_ON(phymode == MODE_UNKNOWN);
+@@ -3677,8 +3679,8 @@ static void ath10k_bss_assoc(struct ieee
+ /* ap_sta must be accessed only within rcu section which must be left
+ * before calling ath10k_setup_peer_smps() which might sleep.
+ */
+- ht_cap = ap_sta->ht_cap;
+- vht_cap = ap_sta->vht_cap;
++ ht_cap = ap_sta->deflink.ht_cap;
++ vht_cap = ap_sta->deflink.vht_cap;
+
+ ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
+ if (ret) {
+@@ -3713,11 +3715,11 @@ static void ath10k_bss_assoc(struct ieee
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d up (associated) bssid %pM aid %d bandwidth %d\n",
+- arvif->vdev_id, bss_conf->bssid, bss_conf->aid, ap_sta->bandwidth);
++ arvif->vdev_id, bss_conf->bssid, vif->cfg.aid, ap_sta->deflink.bandwidth);
+
+ WARN_ON(arvif->is_up);
+
+- arvif->aid = bss_conf->aid;
++ arvif->aid = vif->cfg.aid;
+ ether_addr_copy(arvif->bssid, bss_conf->bssid);
+
+ ret = ath10k_wmi_pdev_set_param(ar,
+@@ -4022,7 +4024,7 @@ static int ath10k_station_assoc(struct a
+ */
+ if (!reassoc) {
+ ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
+- &sta->ht_cap);
++ &sta->deflink.ht_cap);
+ if (ret) {
+ ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+@@ -6916,7 +6918,7 @@ static void ath10k_recalculate_mgmt_rate
+ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
+@@ -6930,7 +6932,7 @@ static void ath10k_bss_info_changed(stru
+ mutex_lock(&ar->conf_mutex);
+
+ if (changed & BSS_CHANGED_IBSS)
+- ath10k_control_ibss(arvif, info, vif->addr);
++ ath10k_control_ibss(arvif, vif);
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ arvif->beacon_interval = info->beacon_int;
+@@ -6995,9 +6997,9 @@ static void ath10k_bss_info_changed(stru
+
+ if (changed & BSS_CHANGED_SSID &&
+ vif->type == NL80211_IFTYPE_AP) {
+- arvif->u.ap.ssid_len = info->ssid_len;
+- if (info->ssid_len)
+- memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len);
++ arvif->u.ap.ssid_len = vif->cfg.ssid_len;
++ if (vif->cfg.ssid_len)
++ memcpy(arvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len);
+ arvif->u.ap.hidden_ssid = info->hidden_ssid;
+ }
+
+@@ -7074,7 +7076,7 @@ static void ath10k_bss_info_changed(stru
+ }
+
+ if (changed & BSS_CHANGED_ASSOC) {
+- if (info->assoc) {
++ if (vif->cfg.assoc) {
+ /* Workaround: Make sure monitor vdev is not running
+ * when associating to prevent some firmware revisions
+ * (e.g. 10.1 and 10.2) from crashing.
+@@ -7099,7 +7101,7 @@ static void ath10k_bss_info_changed(stru
+ }
+
+ if (changed & BSS_CHANGED_PS) {
+- arvif->ps = vif->bss_conf.ps;
++ arvif->ps = vif->cfg.ps;
+
+ ret = ath10k_config_ps(ar);
+ if (ret)
+@@ -7699,7 +7701,7 @@ static void ath10k_sta_rc_update_wk(stru
+
+ if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+ ath10k_dbg(ar, ATH10K_DBG_STA, "mac update sta %pM supp rates, bandwidth: %d\n",
+- sta->addr, sta->bandwidth);
++ sta->addr, sta->deflink.bandwidth);
+
+ err = ath10k_station_assoc(ar, arvif->vif, sta, true);
+ if (err)
+@@ -7751,10 +7753,10 @@ static int ath10k_sta_set_txpwr(struct i
+ int ret = 0;
+ s16 txpwr;
+
+- if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC) {
++ if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) {
+ txpwr = 0;
+ } else {
+- txpwr = sta->txpwr.power;
++ txpwr = sta->deflink.txpwr.power;
+ if (!txpwr)
+ return -EINVAL;
+ }
+@@ -7874,26 +7876,29 @@ static int ath10k_mac_validate_rate_mask
+ struct ieee80211_sta *sta,
+ u32 rate_ctrl_flag, u8 nss)
+ {
+- if (nss > sta->rx_nss) {
++ struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
++ struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
++
++ if (nss > sta->deflink.rx_nss) {
+ ath10k_warn(ar, "Invalid nss field, configured %u limit %u\n",
+- nss, sta->rx_nss);
++ nss, sta->deflink.rx_nss);
+ return -EINVAL;
+ }
+
+ if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_VHT) {
+- if (!sta->vht_cap.vht_supported) {
++ if (!vht_cap->vht_supported) {
+ ath10k_warn(ar, "Invalid VHT rate for sta %pM\n",
+ sta->addr);
+ return -EINVAL;
+ }
+ } else if (ATH10K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HT) {
+- if (!sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
++ if (!ht_cap->ht_supported || vht_cap->vht_supported) {
+ ath10k_warn(ar, "Invalid HT rate for sta %pM\n",
+ sta->addr);
+ return -EINVAL;
+ }
+ } else {
+- if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported)
++ if (ht_cap->ht_supported || vht_cap->vht_supported)
+ return -EINVAL;
+ }
+
+@@ -8567,7 +8572,7 @@ static int ath10k_sta_state(struct ieee8
+ * New association.
+ */
+ ath10k_dbg(ar, ATH10K_DBG_STA, "mac sta %pM associated, bandwidth: %d\n",
+- sta->addr, sta->bandwidth);
++ sta->addr, sta->deflink.bandwidth);
+
+ ret = ath10k_station_assoc(ar, vif, sta, false);
+ if (ret)
+@@ -8580,7 +8585,7 @@ static int ath10k_sta_state(struct ieee8
+ * Tdls station authorized.
+ */
+ ath10k_dbg(ar, ATH10K_DBG_STA, "mac tdls sta %pM authorized, bandwidth: %d\n",
+- sta->addr, sta->bandwidth);
++ sta->addr, sta->deflink.bandwidth);
+
+ ret = ath10k_station_assoc(ar, vif, sta, false);
+ if (ret) {
+@@ -8721,8 +8726,8 @@ exit:
+ return ret;
+ }
+
+-static int ath10k_conf_tx(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif, u16 ac,
++static int ath10k_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ unsigned int link_id, u16 ac,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct ath10k *ar = hw->priv;
+@@ -9308,7 +9313,7 @@ static bool ath10k_mac_set_vht_bitrate_m
+ u8 rate = arvif->vht_pfr;
+
+ /* skip non vht and multiple rate peers */
+- if (!sta->vht_cap.vht_supported || arvif->vht_num_rates != 1)
++ if (!sta->deflink.vht_cap.vht_supported || arvif->vht_num_rates != 1)
+ return false;
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+@@ -9349,7 +9354,7 @@ static void ath10k_mac_clr_bitrate_mask_
+ int err;
+
+ /* clear vht peers only */
+- if (arsta->arvif != arvif || !sta->vht_cap.vht_supported)
++ if (arsta->arvif != arvif || !sta->deflink.vht_cap.vht_supported)
+ return;
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+@@ -9534,13 +9539,13 @@ static void ath10k_sta_rc_update(struct
+
+ ath10k_dbg(ar, ATH10K_DBG_STA,
+ "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+- sta->addr, changed, sta->bandwidth, sta->rx_nss,
+- sta->smps_mode);
++ sta->addr, changed, sta->deflink.bandwidth, sta->deflink.rx_nss,
++ sta->deflink.smps_mode);
+
+ if (changed & IEEE80211_RC_BW_CHANGED) {
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+
+- switch (sta->bandwidth) {
++ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_20:
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+@@ -9555,7 +9560,7 @@ static void ath10k_sta_rc_update(struct
+ break;
+ default:
+ ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n",
+- sta->bandwidth, sta->addr);
++ sta->deflink.bandwidth, sta->addr);
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+ }
+@@ -9564,12 +9569,12 @@ static void ath10k_sta_rc_update(struct
+ }
+
+ if (changed & IEEE80211_RC_NSS_CHANGED)
+- arsta->nss = sta->rx_nss;
++ arsta->nss = sta->deflink.rx_nss;
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED) {
+ smps = WMI_PEER_SMPS_PS_NONE;
+
+- switch (sta->smps_mode) {
++ switch (sta->deflink.smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_OFF:
+ smps = WMI_PEER_SMPS_PS_NONE;
+@@ -9582,7 +9587,7 @@ static void ath10k_sta_rc_update(struct
+ break;
+ case IEEE80211_SMPS_NUM_MODES:
+ ath10k_warn(ar, "Invalid smps %d in sta rc update for %pM\n",
+- sta->smps_mode, sta->addr);
++ sta->deflink.smps_mode, sta->addr);
+ smps = WMI_PEER_SMPS_PS_NONE;
+ break;
+ }
+@@ -9896,7 +9901,7 @@ ath10k_mac_change_chanctx_cnt_iter(void
+ {
+ struct ath10k_mac_change_chanctx_arg *arg = data;
+
+- if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx)
++ if (rcu_access_pointer(vif->bss_conf.chanctx_conf) != arg->ctx)
+ return;
+
+ arg->n_vifs++;
+@@ -9909,7 +9914,7 @@ ath10k_mac_change_chanctx_fill_iter(void
+ struct ath10k_mac_change_chanctx_arg *arg = data;
+ struct ieee80211_chanctx_conf *ctx;
+
+- ctx = rcu_access_pointer(vif->chanctx_conf);
++ ctx = rcu_access_pointer(vif->bss_conf.chanctx_conf);
+ if (ctx != arg->ctx)
+ return;
+
+@@ -9982,6 +9987,7 @@ unlock:
+ static int
+ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+ {
+ struct ath10k *ar = hw->priv;
+@@ -10061,6 +10067,7 @@ err:
+ static void
+ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+ {
+ struct ath10k *ar = hw->priv;
+--- a/ath10k-5.15/txrx.c
++++ b/ath10k-5.15/txrx.c
+@@ -260,7 +260,7 @@ int ath10k_txrx_tx_unref(struct ath10k_h
+ nf = ar->debug.nf_sum[0];
+ #endif
+ info->status.ack_signal = nf + tx_done->ack_rssi;
+- info->status.is_valid_ack_signal = true;
++ info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID;
+ }
+
+ if (tx_done->tx_rate_code || tx_done->tx_rate_flags || ar->ok_tx_rate_status) {
+--- a/ath10k-5.15/wmi.c
++++ b/ath10k-5.15/wmi.c
+@@ -2587,7 +2587,7 @@ wmi_process_mgmt_tx_comp(struct ath10k *
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.ack_signal = ath10k_get_noisefloor(0, ar) +
+ param->ack_rssi;
+- info->status.is_valid_ack_signal = true;
++ info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID;
+ }
+
+ ieee80211_tx_status_irqsafe(ar->hw, msdu);
+@@ -4258,13 +4258,13 @@ void ath10k_wmi_event_host_swba(struct a
+ * Once CSA counter is completed stop sending beacons until
+ * actual channel switch is done
+ */
+- if (arvif->vif->csa_active &&
++ if (arvif->vif->bss_conf.csa_active &&
+ ieee80211_beacon_cntdwn_is_complete(arvif->vif)) {
+ ieee80211_csa_finish(arvif->vif);
+ continue;
+ }
+
+- bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
++ bcn = ieee80211_beacon_get(ar->hw, arvif->vif, 0);
+ if (!bcn) {
+ ath10k_warn(ar, "could not get mac80211 beacon, vdev_id: %i addr: %pM\n",
+ arvif->vdev_id, arvif->vif->addr);
+--- a/ath10k-5.15/htt_rx.c
++++ b/ath10k-5.15/htt_rx.c
+@@ -4017,7 +4017,7 @@ ath10k_update_per_peer_tx_stats(struct a
+ switch (txrate.flags) {
+ case WMI_RATE_PREAMBLE_OFDM:
+ if (arsta->arvif && arsta->arvif->vif)
+- conf = rcu_dereference(arsta->arvif->vif->chanctx_conf);
++ conf = rcu_dereference(arsta->arvif->vif->bss_conf.chanctx_conf);
+ if (conf && conf->def.chan->band == NL80211_BAND_5GHZ)
+ arsta->tx_info.status.rates[0].idx = rate_idx - 4;
+ break;
+--- a/ath10k-5.15/wmi-tlv.c
++++ b/ath10k-5.15/wmi-tlv.c
+@@ -205,7 +205,7 @@ static int ath10k_wmi_tlv_event_bcn_tx_s
+ }
+
+ arvif = ath10k_get_arvif(ar, vdev_id);
+- if (arvif && arvif->is_up && arvif->vif->csa_active)
++ if (arvif && arvif->is_up && arvif->vif->bss_conf.csa_active)
+ ieee80211_queue_work(ar->hw, &arvif->ap_csa_work);
+
+ kfree(tb);
+--- a/ath10k-5.15/core.c
++++ b/ath10k-5.15/core.c
+@@ -4081,7 +4081,7 @@ static int ath10k_core_probe_fw(struct a
+ ath10k_debug_print_board_info(ar);
+ }
+
+- device_get_mac_address(ar->dev, ar->mac_addr, sizeof(ar->mac_addr));
++ device_get_mac_address(ar->dev, ar->mac_addr);
+
+ /* Try to get mac address from device node (from nvmem cell) */
+ of_get_mac_address(ar->dev->of_node, ar->mac_addr);
+--- a/ath10k-5.15/pci.c
++++ b/ath10k-5.15/pci.c
+@@ -3547,8 +3547,7 @@ static void ath10k_pci_free_irq(struct a
+
+ void ath10k_pci_init_napi(struct ath10k *ar)
+ {
+- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll,
+- ATH10K_NAPI_BUDGET);
++ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll);
+ }
+
+ static int ath10k_pci_init_irq(struct ath10k *ar)
+--- a/ath10k-5.15/sdio.c
++++ b/ath10k-5.15/sdio.c
+@@ -2531,8 +2531,7 @@ static int ath10k_sdio_probe(struct sdio
+ return -ENOMEM;
+ }
+
+- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll,
+- ATH10K_NAPI_BUDGET);
++ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
+--- a/ath10k-5.15/snoc.c
++++ b/ath10k-5.15/snoc.c
+@@ -1242,8 +1242,7 @@ static int ath10k_snoc_napi_poll(struct
+
+ static void ath10k_snoc_init_napi(struct ath10k *ar)
+ {
+- netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll,
+- ATH10K_NAPI_BUDGET);
++ netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll);
+ }
+
+ static int ath10k_snoc_request_irq(struct ath10k *ar)
if (ret)
--- a/ath10k-5.15/mac.c
+++ b/ath10k-5.15/mac.c
-@@ -11544,7 +11544,7 @@ int ath10k_mac_register(struct ath10k *a
+@@ -11551,7 +11551,7 @@ int ath10k_mac_register(struct ath10k *a
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
#ifdef CPTCFG_MAC80211_LEDS
PKG_NAME:=mac80211
-PKG_VERSION:=5.15.81-1
+PKG_VERSION:=6.1-rc8
PKG_RELEASE:=1
-PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.15.81/
-PKG_HASH:=5227d3c35ccebacfaee6b8180b3a87b9910f3c94ee768ebc5c0fef3c86b6146d
+# PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.15.58/
+PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/
+PKG_HASH:=7f3d96c2573183cd79d6a3ebe5e1b7b73c19d1326d443c85b69c4181f14e6e2b
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/backports-$(PKG_VERSION)
help
--- a/local-symbols
+++ b/local-symbols
-@@ -106,6 +106,7 @@ ADM8211=
+@@ -110,6 +110,7 @@ ADM8211=
ATH_COMMON=
WLAN_VENDOR_ATH=
ATH_DEBUG=
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
-@@ -3315,6 +3315,8 @@ void regulatory_hint_country_ie(struct w
+@@ -3370,6 +3370,8 @@ void regulatory_hint_country_ie(struct w
enum environment_cap env = ENVIRON_ANY;
struct regulatory_request *request = NULL, *lr;
/* IE len must be evenly divisible by 2 */
if (country_ie_len & 0x01)
return;
-@@ -3566,6 +3568,7 @@ static bool is_wiphy_all_set_reg_flag(en
+@@ -3621,6 +3623,7 @@ static bool is_wiphy_all_set_reg_flag(en
void regulatory_hint_disconnect(void)
{
FRANCE_RES = 0x31,
FCC3_FCCA = 0x3A,
FCC3_WORLD = 0x3B,
-@@ -172,6 +173,7 @@ static struct reg_dmn_pair_mapping regDo
+@@ -173,6 +174,7 @@ static struct reg_dmn_pair_mapping regDo
{FCC2_WORLD, CTL_FCC, CTL_ETSI},
{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
{FCC3_FCCA, CTL_FCC, CTL_FCC},
{FCC3_WORLD, CTL_FCC, CTL_ETSI},
{FCC3_ETSIC, CTL_FCC, CTL_ETSI},
{FCC4_FCCA, CTL_FCC, CTL_FCC},
-@@ -483,6 +485,7 @@ static struct country_code_to_enum_rd al
+@@ -486,6 +488,7 @@ static struct country_code_to_enum_rd al
{CTRY_UAE, NULL1_WORLD, "AE"},
{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
--- a/local-symbols
+++ b/local-symbols
-@@ -165,6 +165,7 @@ ATH10K_SNOC=
+@@ -169,6 +169,7 @@ ATH10K_SNOC=
ATH10K_DEBUG=
ATH10K_DEBUGFS=
ATH10K_SPECTRAL=
+++ /dev/null
-From 2587d5198aa5adcbd8896aae4a2404dc13d48637 Mon Sep 17 00:00:00 2001
-From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Date: Wed, 18 May 2022 10:27:26 +0300
-Subject: ath10k: improve tx status reporting
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-We use ieee80211_tx_status() to report each completed tx frame.
-Internally, this function calls sta_info_get_by_addrs(), what has a
-couple of drawbacks:
-1. additional station lookup causes a performance degradation;
-2. mac80211 can not properly account Ethernet encapsulated frames due
- to the inability to properly determine the destination (station) MAC
- address since ieee80211_tx_status() assumes the frame has a 802.11
- header.
-
-The latter is especially destructive if we want to use hardware frames
-encapsulation.
-
-To fix both of these issues, replace ieee80211_tx_status() with
-ieee80211_tx_status_ext() call and feed it station pointer from the tx
-queue associated with the transmitted frame.
-
-Tested-on: QCA9888 hw2.0 PCI 10.4-3.9.0.2-00131
-Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00157-QCARMSWPZ-1
-
-Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Tested-by: Oldřich Jedlička <oldium.pro@gmail.com> # TP-Link Archer C7 v4 & v5 (QCA9563 + QCA9880)
-Tested-by: Edward Matijevic <motolav@gmail.com> # TP-Link Archer C2600 (IPQ8064 + QCA9980 10.4.1.00030-1)
-Tested-by: Edward Matijevic <motolav@gmail.com> # QCA9377 PCI in Sta mode
-Tested-by: Zhijun You <hujy652@gmail.com> # NETGEAR R7800 (QCA9984 10.4-3.9.0.2-00159)
-Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
-Link: https://lore.kernel.org/r/20220516032519.29831-2-ryazanov.s.a@gmail.com
----
- drivers/net/wireless/ath/ath10k/txrx.c | 15 ++++++++++++++-
- 1 file changed, 14 insertions(+), 1 deletion(-)
-
---- a/drivers/net/wireless/ath/ath10k/txrx.c
-+++ b/drivers/net/wireless/ath/ath10k/txrx.c
-@@ -43,6 +43,7 @@ out:
- int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
- const struct htt_tx_done *tx_done)
- {
-+ struct ieee80211_tx_status status;
- struct ath10k *ar = htt->ar;
- struct device *dev = ar->dev;
- struct ieee80211_tx_info *info;
-@@ -128,7 +129,19 @@ int ath10k_txrx_tx_unref(struct ath10k_h
- info->status.is_valid_ack_signal = true;
- }
-
-- ieee80211_tx_status(htt->ar->hw, msdu);
-+ memset(&status, 0, sizeof(status));
-+ status.skb = msdu;
-+ status.info = info;
-+
-+ rcu_read_lock();
-+
-+ if (txq)
-+ status.sta = txq->sta;
-+
-+ ieee80211_tx_status_ext(htt->ar->hw, &status);
-+
-+ rcu_read_unlock();
-+
- /* we do not own the msdu anymore */
-
- return 0;
+++ /dev/null
-From a09740548275a74b897654b3aca5af589289b57a Mon Sep 17 00:00:00 2001
-From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Date: Mon, 16 May 2022 13:26:00 +0300
-Subject: ath10k: turn rawmode into frame_mode
-
-Turn boolean rawmode module param into integer frame_mode param that
-contains value from ath10k_hw_txrx_mode enum. As earlier the default
-param value is non-RAW (native Wi-Fi) encapsulation. The param name
-is selected to be consistent with the similar ath11k param.
-
-This is a preparation step for upcoming encapsulation offloading
-support.
-
-Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
-Link: https://lore.kernel.org/r/20220516032519.29831-4-ryazanov.s.a@gmail.com
----
- drivers/net/wireless/ath/ath10k/core.c | 11 +++++++----
- drivers/net/wireless/ath/ath10k/core.h | 1 +
- 2 files changed, 8 insertions(+), 4 deletions(-)
-
---- a/drivers/net/wireless/ath/ath10k/core.c
-+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -32,9 +32,11 @@ EXPORT_SYMBOL(ath10k_debug_mask);
- static unsigned int ath10k_cryptmode_param;
- static bool uart_print;
- static bool skip_otp;
--static bool rawmode;
- static bool fw_diag_log;
-
-+/* frame mode values are mapped as per enum ath10k_hw_txrx_mode */
-+unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI;
-+
- unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) |
- BIT(ATH10K_FW_CRASH_DUMP_CE_DATA);
-
-@@ -43,15 +45,16 @@ module_param_named(debug_mask, ath10k_de
- module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644);
- module_param(uart_print, bool, 0644);
- module_param(skip_otp, bool, 0644);
--module_param(rawmode, bool, 0644);
- module_param(fw_diag_log, bool, 0644);
-+module_param_named(frame_mode, ath10k_frame_mode, uint, 0644);
- module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444);
-
- MODULE_PARM_DESC(debug_mask, "Debugging mask");
- MODULE_PARM_DESC(uart_print, "Uart target debugging");
- MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
- MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software");
--MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath");
-+MODULE_PARM_DESC(frame_mode,
-+ "Datapath frame mode (0: raw, 1: native wifi (default))");
- MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file");
- MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging");
-
-@@ -2487,7 +2490,7 @@ static int ath10k_core_init_firmware_fea
- ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT;
- ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT;
-
-- if (rawmode) {
-+ if (ath10k_frame_mode == ATH10K_HW_TXRX_RAW) {
- if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
- fw_file->fw_features)) {
- ath10k_err(ar, "rawmode = 1 requires support from firmware");
---- a/drivers/net/wireless/ath/ath10k/core.h
-+++ b/drivers/net/wireless/ath/ath10k/core.h
-@@ -1311,6 +1311,7 @@ static inline bool ath10k_peer_stats_ena
- return false;
- }
-
-+extern unsigned int ath10k_frame_mode;
- extern unsigned long ath10k_coredump_mask;
-
- void ath10k_core_napi_sync_disable(struct ath10k *ar);
+++ /dev/null
-From 70f119fb82af7f7417dc659faf02c91e1f853739 Mon Sep 17 00:00:00 2001
-From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Date: Mon, 16 May 2022 13:26:00 +0300
-Subject: ath10k: htt_tx: do not interpret Eth frames as WiFi
-
-The xmit path for the Ethernet encapsulated frames become more or less
-usable since d740d8fd2439 ("ath10k: unify tx mode and dispatch"). This
-change reorganize the xmit path in a manageable way to properly support
-various tx modes, but misses that the Ethernet encapsulated frame is a
-special case. We do not have an IEEE 802.11 header at the begining of
-them. But the HTT Tx handler still interprets first bytes of each frame
-as an IEEE 802.11 Frame Control field.
-
-Than this code was copied by e62ee5c381c5 ("ath10k: Add support for
-htt_data_tx_desc_64 descriptor") and a2097d6444c3 ("ath10k: htt: High
-latency TX support") to another handlers. In fact the issue in the high
-latency (HL) handler was introduced by 83ac260151e7 ("ath10k: add mic
-bytes for pmf management packet").
-
-Ethernet encapsulated frame tx mode stay unused until 75d85fd9993c
-("ath10k: introduce basic tdls functionality") started using it for TDLS
-frames to avoid key selection issue in some firmwares.
-
-Trying to interpret the begining of an Ethernet encapsulated frame as an
-IEEE 802.11 header was not hurt us noticeably since we need to meet two
-conditions: (1) xmit should be performed towards a TDLS peer, and (2)
-the TDLS peer should have a specific OUI part of its MAC address. Looks
-like that the rareness in TDLS communications of OUIs that can be
-interpreted as an 802.11 management frame saves users from facing this
-issue earlier.
-
-Improve Ethernet tx mode support in the HTT Tx handler by avoiding
-interpreting its first bytes as an IEEE 802.11 header. While at it, make
-the ieee80211_hdr variable local to the code block that is guarded by
-!is_eth check. In this way, we clarify in which cases a frame can be
-interpreted as IEEE 802.11, and saves us from similar issues in the
-future.
-
-Credits: this change as part of xmit encapsulation offloading support
-was originally made by QCA and then submitted for inclusion by John
-Crispin [1]. But the whole work was not accepted due to the lack of a
-part for 64-bits descriptors [2]. Zhijun You then pointed this out to me
-in a reply to my initial RFC patch series. And I made this slightly
-reworked version that covered all the HTT Tx handler variants.
-
-1. https://lore.kernel.org/all/20191216092207.31032-1-john@phrozen.org/
-2. https://patchwork.kernel.org/project/linux-wireless/patch/20191216092207.31032-1-john@phrozen.org/
-
-Reported-by: Zhijun You <hujy652@gmail.com>
-Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
-Signed-off-by: John Crispin <john@phrozen.org>
-Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
-Link: https://lore.kernel.org/r/20220516032519.29831-3-ryazanov.s.a@gmail.com
----
- drivers/net/wireless/ath/ath10k/htt_tx.c | 61 ++++++++++++++++++--------------
- 1 file changed, 35 insertions(+), 26 deletions(-)
-
---- a/drivers/net/wireless/ath/ath10k/htt_tx.c
-+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
-@@ -1295,7 +1295,6 @@ static int ath10k_htt_tx_hl(struct ath10
- struct ath10k *ar = htt->ar;
- int res, data_len;
- struct htt_cmd_hdr *cmd_hdr;
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
- struct htt_data_tx_desc *tx_desc;
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- struct sk_buff *tmp_skb;
-@@ -1306,11 +1305,15 @@ static int ath10k_htt_tx_hl(struct ath10
- u16 flags1 = 0;
- u16 msdu_id = 0;
-
-- if ((ieee80211_is_action(hdr->frame_control) ||
-- ieee80211_is_deauth(hdr->frame_control) ||
-- ieee80211_is_disassoc(hdr->frame_control)) &&
-- ieee80211_has_protected(hdr->frame_control)) {
-- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ if (!is_eth) {
-+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
-+
-+ if ((ieee80211_is_action(hdr->frame_control) ||
-+ ieee80211_is_deauth(hdr->frame_control) ||
-+ ieee80211_is_disassoc(hdr->frame_control)) &&
-+ ieee80211_has_protected(hdr->frame_control)) {
-+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ }
- }
-
- data_len = msdu->len;
-@@ -1407,7 +1410,6 @@ static int ath10k_htt_tx_32(struct ath10
- {
- struct ath10k *ar = htt->ar;
- struct device *dev = ar->dev;
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- struct ath10k_hif_sg_item sg_items[2];
-@@ -1439,15 +1441,19 @@ static int ath10k_htt_tx_32(struct ath10
- txbuf_paddr = htt->txbuf.paddr +
- (sizeof(struct ath10k_htt_txbuf_32) * msdu_id);
-
-- if ((ieee80211_is_action(hdr->frame_control) ||
-- ieee80211_is_deauth(hdr->frame_control) ||
-- ieee80211_is_disassoc(hdr->frame_control)) &&
-- ieee80211_has_protected(hdr->frame_control)) {
-- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-- } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
-- txmode == ATH10K_HW_TXRX_RAW &&
-- ieee80211_has_protected(hdr->frame_control)) {
-- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ if (!is_eth) {
-+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
-+
-+ if ((ieee80211_is_action(hdr->frame_control) ||
-+ ieee80211_is_deauth(hdr->frame_control) ||
-+ ieee80211_is_disassoc(hdr->frame_control)) &&
-+ ieee80211_has_protected(hdr->frame_control)) {
-+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
-+ txmode == ATH10K_HW_TXRX_RAW &&
-+ ieee80211_has_protected(hdr->frame_control)) {
-+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ }
- }
-
- skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
-@@ -1609,7 +1615,6 @@ static int ath10k_htt_tx_64(struct ath10
- {
- struct ath10k *ar = htt->ar;
- struct device *dev = ar->dev;
-- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- struct ath10k_hif_sg_item sg_items[2];
-@@ -1641,15 +1646,19 @@ static int ath10k_htt_tx_64(struct ath10
- txbuf_paddr = htt->txbuf.paddr +
- (sizeof(struct ath10k_htt_txbuf_64) * msdu_id);
-
-- if ((ieee80211_is_action(hdr->frame_control) ||
-- ieee80211_is_deauth(hdr->frame_control) ||
-- ieee80211_is_disassoc(hdr->frame_control)) &&
-- ieee80211_has_protected(hdr->frame_control)) {
-- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-- } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
-- txmode == ATH10K_HW_TXRX_RAW &&
-- ieee80211_has_protected(hdr->frame_control)) {
-- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ if (!is_eth) {
-+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
-+
-+ if ((ieee80211_is_action(hdr->frame_control) ||
-+ ieee80211_is_deauth(hdr->frame_control) ||
-+ ieee80211_is_disassoc(hdr->frame_control)) &&
-+ ieee80211_has_protected(hdr->frame_control)) {
-+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
-+ txmode == ATH10K_HW_TXRX_RAW &&
-+ ieee80211_has_protected(hdr->frame_control)) {
-+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
-+ }
- }
-
- skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+++ /dev/null
-From af6d8265c47e46881b80c6b073f53c8c4af52d28 Mon Sep 17 00:00:00 2001
-From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Date: Mon, 16 May 2022 13:26:00 +0300
-Subject: ath10k: add encapsulation offloading support
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Frame encapsulation from Ethernet into the IEEE 802.11 frame format
-takes a considerable host CPU time on the xmit path. The firmware is
-able to do this operation for us, so enable encapsulation offloading for
-AP and Sta interface types to improve overall system performance.
-
-The driver is almost ready for encapsulation offloading support. There
-are only a few places where the driver assumes the frame format is IEEE
-802.11 that need to be fixed.
-
-Encapsulation offloading is currently disabled by default and the driver
-utilizes mac80211 encapsulation support. To activate offloading, the
-frame_mode=2 parameter should be passed during module loading.
-
-On a QCA9563+QCA9888-based access point in bridged mode, encapsulation
-offloading increases TCP 16-streams DL throughput from 365 to 396 mbps
-(+8%) and UDP DL throughput from 436 to 483 mbps (+11%).
-
-Tested-on: QCA9888 hw2.0 PCI 10.4-3.9.0.2-00131
-Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00157-QCARMSWPZ-1
-Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
-Tested-by: Oldřich Jedlička <oldium.pro@gmail.com> # TP-Link Archer C7 v4 & v5 (QCA9563 + QCA9880)
-Tested-by: Edward Matijevic <motolav@gmail.com> # TP-Link Archer C2600 (IPQ8064 + QCA9980 10.4.1.00030-1)
-Tested-by: Edward Matijevic <motolav@gmail.com> # QCA9377 PCI in Sta mode
-Tested-by: Zhijun You <hujy652@gmail.com> # NETGEAR R7800 (QCA9984 10.4-3.9.0.2-00159)
-Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
-Link: https://lore.kernel.org/r/20220516032519.29831-5-ryazanov.s.a@gmail.com
----
- drivers/net/wireless/ath/ath10k/core.c | 2 +-
- drivers/net/wireless/ath/ath10k/mac.c | 67 +++++++++++++++++++++++++++-------
- 2 files changed, 55 insertions(+), 14 deletions(-)
-
---- a/drivers/net/wireless/ath/ath10k/core.c
-+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -54,7 +54,7 @@ MODULE_PARM_DESC(uart_print, "Uart targe
- MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
- MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software");
- MODULE_PARM_DESC(frame_mode,
-- "Datapath frame mode (0: raw, 1: native wifi (default))");
-+ "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)");
- MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file");
- MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging");
-
---- a/drivers/net/wireless/ath/ath10k/mac.c
-+++ b/drivers/net/wireless/ath/ath10k/mac.c
-@@ -3717,6 +3717,9 @@ ath10k_mac_tx_h_get_txmode(struct ath10k
- const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
- __le16 fc = hdr->frame_control;
-
-+ if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
-+ return ATH10K_HW_TXRX_ETHERNET;
-+
- if (!vif || vif->type == NL80211_IFTYPE_MONITOR)
- return ATH10K_HW_TXRX_RAW;
-
-@@ -3877,6 +3880,12 @@ static void ath10k_mac_tx_h_fill_cb(stru
- bool noack = false;
-
- cb->flags = 0;
-+
-+ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
-+ cb->flags |= ATH10K_SKB_F_QOS; /* Assume data frames are QoS */
-+ goto finish_cb_fill;
-+ }
-+
- if (!ath10k_tx_h_use_hwcrypto(vif, skb))
- cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
-
-@@ -3915,6 +3924,7 @@ static void ath10k_mac_tx_h_fill_cb(stru
- cb->flags |= ATH10K_SKB_F_RAW_TX;
- }
-
-+finish_cb_fill:
- cb->vif = vif;
- cb->txq = txq;
- cb->airtime_est = airtime;
-@@ -4038,7 +4048,11 @@ static int ath10k_mac_tx(struct ath10k *
- ath10k_tx_h_seq_no(vif, skb);
- break;
- case ATH10K_HW_TXRX_ETHERNET:
-- ath10k_tx_h_8023(skb);
-+ /* Convert 802.11->802.3 header only if the frame was erlier
-+ * encapsulated to 802.11 by mac80211. Otherwise pass it as is.
-+ */
-+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
-+ ath10k_tx_h_8023(skb);
- break;
- case ATH10K_HW_TXRX_RAW:
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) &&
-@@ -4650,12 +4664,10 @@ static void ath10k_mac_op_tx(struct ieee
- struct ieee80211_vif *vif = info->control.vif;
- struct ieee80211_sta *sta = control->sta;
- struct ieee80211_txq *txq = NULL;
-- struct ieee80211_hdr *hdr = (void *)skb->data;
- enum ath10k_hw_txrx_mode txmode;
- enum ath10k_mac_tx_path txpath;
- bool is_htt;
- bool is_mgmt;
-- bool is_presp;
- int ret;
- u16 airtime;
-
-@@ -4669,8 +4681,14 @@ static void ath10k_mac_op_tx(struct ieee
- is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT);
-
- if (is_htt) {
-+ bool is_presp = false;
-+
- spin_lock_bh(&ar->htt.tx_lock);
-- is_presp = ieee80211_is_probe_resp(hdr->frame_control);
-+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
-+ struct ieee80211_hdr *hdr = (void *)skb->data;
-+
-+ is_presp = ieee80211_is_probe_resp(hdr->frame_control);
-+ }
-
- ret = ath10k_htt_tx_inc_pending(htt);
- if (ret) {
-@@ -5470,6 +5488,30 @@ static int ath10k_mac_set_txbf_conf(stru
- ar->wmi.vdev_param->txbf, value);
- }
-
-+static void ath10k_update_vif_offload(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif)
-+{
-+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
-+ struct ath10k *ar = hw->priv;
-+ u32 vdev_param;
-+ int ret;
-+
-+ if (ath10k_frame_mode != ATH10K_HW_TXRX_ETHERNET ||
-+ ar->wmi.vdev_param->tx_encap_type == WMI_VDEV_PARAM_UNSUPPORTED ||
-+ (vif->type != NL80211_IFTYPE_STATION &&
-+ vif->type != NL80211_IFTYPE_AP))
-+ vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
-+
-+ vdev_param = ar->wmi.vdev_param->tx_encap_type;
-+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
-+ ATH10K_HW_TXRX_NATIVE_WIFI);
-+ /* 10.X firmware does not support this VDEV parameter. Do not warn */
-+ if (ret && ret != -EOPNOTSUPP) {
-+ ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
-+ arvif->vdev_id, ret);
-+ }
-+}
-+
- /*
- * TODO:
- * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
-@@ -5679,15 +5721,7 @@ static int ath10k_add_interface(struct i
-
- arvif->def_wep_key_idx = -1;
-
-- vdev_param = ar->wmi.vdev_param->tx_encap_type;
-- ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
-- ATH10K_HW_TXRX_NATIVE_WIFI);
-- /* 10.X firmware does not support this VDEV parameter. Do not warn */
-- if (ret && ret != -EOPNOTSUPP) {
-- ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
-- arvif->vdev_id, ret);
-- goto err_vdev_delete;
-- }
-+ ath10k_update_vif_offload(hw, vif);
-
- /* Configuring number of spatial stream for monitor interface is causing
- * target assert in qca9888 and qca6174.
-@@ -9372,6 +9406,7 @@ static const struct ieee80211_ops ath10k
- .stop = ath10k_stop,
- .config = ath10k_config,
- .add_interface = ath10k_add_interface,
-+ .update_vif_offload = ath10k_update_vif_offload,
- .remove_interface = ath10k_remove_interface,
- .configure_filter = ath10k_configure_filter,
- .bss_info_changed = ath10k_bss_info_changed,
-@@ -10041,6 +10076,12 @@ int ath10k_mac_register(struct ath10k *a
- if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map))
- ieee80211_hw_set(ar->hw, SUPPORTS_TDLS_BUFFER_STA);
-
-+ if (ath10k_frame_mode == ATH10K_HW_TXRX_ETHERNET) {
-+ if (ar->wmi.vdev_param->tx_encap_type !=
-+ WMI_VDEV_PARAM_UNSUPPORTED)
-+ ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
-+ }
-+
- ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
- ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
- ar->hw->wiphy->max_remain_on_channel_duration = 5000;
+++ /dev/null
-From f2a7064a78b22f2b68b9fcbc8a6f4c5e61c5ba64 Mon Sep 17 00:00:00 2001
-From: Robert Marko <robimarko@gmail.com>
-Date: Sun, 10 Oct 2021 00:17:11 +0200
-Subject: [PATCH] ath10k: support bus and device specific API 1 BDF selection
-
-Some ath10k IPQ40xx devices like the MikroTik hAP ac2 and ac3 require the
-BDF-s to be extracted from the device storage instead of shipping packaged
-API 2 BDF-s.
-
-This is required as MikroTik has started shipping boards that require BDF-s
-to be updated, as otherwise their WLAN performance really suffers.
-This is however impossible as the devices that require this are release
-under the same revision and its not possible to differentiate them from
-devices using the older BDF-s.
-
-In OpenWrt we are extracting the calibration data during runtime and we are
-able to extract the BDF-s in the same manner, however we cannot package the
-BDF-s to API 2 format on the fly and can only use API 1 to provide BDF-s on
-the fly.
-This is an issue as the ath10k driver explicitly looks only for the
-board.bin file and not for something like board-bus-device.bin like it does
-for pre-cal data.
-Due to this we have no way of providing correct BDF-s on the fly, so lets
-extend the ath10k driver to first look for BDF-s in the
-board-bus-device.bin format, for example: board-ahb-a800000.wifi.bin
-If that fails, look for the default board file name as defined previously.
-
-Signed-off-by: Robert Marko <robimarko@gmail.com>
-Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
-Link: https://lore.kernel.org/r/20211009221711.2315352-1-robimarko@gmail.com
----
- drivers/net/wireless/ath/ath10k/core.c | 13 ++++++++++++-
- 1 file changed, 12 insertions(+), 1 deletion(-)
-
---- a/drivers/net/wireless/ath/ath10k/core.c
-+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -1202,6 +1202,7 @@ success:
- static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type)
- {
- const struct firmware *fw;
-+ char boardname[100];
-
- if (bd_ie_type == ATH10K_BD_IE_BOARD) {
- if (!ar->hw_params.fw.board) {
-@@ -1209,9 +1210,19 @@ static int ath10k_core_fetch_board_data_
- return -EINVAL;
- }
-
-+ scnprintf(boardname, sizeof(boardname), "board-%s-%s.bin",
-+ ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
-+
- ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
- ar->hw_params.fw.dir,
-- ar->hw_params.fw.board);
-+ boardname);
-+ if (IS_ERR(ar->normal_mode_fw.board)) {
-+ fw = ath10k_fetch_fw_file(ar,
-+ ar->hw_params.fw.dir,
-+ ar->hw_params.fw.board);
-+ ar->normal_mode_fw.board = fw;
-+ }
-+
- if (IS_ERR(ar->normal_mode_fw.board))
- return PTR_ERR(ar->normal_mode_fw.board);
-
+++ /dev/null
-From e2333703373e8b81294da5d1c73c30154f75b082 Mon Sep 17 00:00:00 2001
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Fri, 15 Oct 2021 18:56:33 +0200
-Subject: [PATCH] ath10k: fetch (pre-)calibration data via nvmem subsystem
-
-On most embedded ath10k devices (like range extenders,
-routers, accesspoints, ...) the calibration data is
-stored in a easily accessible MTD partitions named
-"ART", "caldata", "calibration", etc...
-
-Since commit 4b361cfa8624 ("mtd: core: add OTP nvmem provider support"):
-MTD partitions and portions of them can be specified
-as potential nvmem-cells which are accessible through
-the nvmem subsystem.
-
-This feature - together with an nvmem cell definition either
-in the platform data or via device-tree allows drivers to get
-the (pre-)calibration data which is required for initializing
-the WIFI.
-
-Tested with Netgear EX6150v2 (IPQ4018)
-
-Cc: Robert Marko <robimarko@gmail.com>
-Cc: Thibaut Varene <hacks@slashdirt.org>
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
----
---- a/drivers/net/wireless/ath/ath10k/core.c
-+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -12,6 +12,7 @@
- #include <linux/dmi.h>
- #include <linux/ctype.h>
- #include <linux/pm_qos.h>
-+#include <linux/nvmem-consumer.h>
- #include <asm/byteorder.h>
-
- #include "core.h"
-@@ -955,7 +956,8 @@ static int ath10k_core_get_board_id_from
- }
-
- if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
-- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE)
-+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE ||
-+ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM)
- bmi_board_id_param = BMI_PARAM_GET_FLASH_BOARD_ID;
- else
- bmi_board_id_param = BMI_PARAM_GET_EEPROM_BOARD_ID;
-@@ -1757,7 +1759,8 @@ static int ath10k_download_and_run_otp(s
-
- /* As of now pre-cal is valid for 10_4 variants */
- if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
-- ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE)
-+ ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE ||
-+ ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM)
- bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL;
-
- ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result);
-@@ -1884,6 +1887,39 @@ out_free:
- return ret;
- }
-
-+static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name)
-+{
-+ struct nvmem_cell *cell;
-+ void *buf;
-+ size_t len;
-+ int ret;
-+
-+ cell = devm_nvmem_cell_get(ar->dev, cell_name);
-+ if (IS_ERR(cell)) {
-+ ret = PTR_ERR(cell);
-+ return ret;
-+ }
-+
-+ buf = nvmem_cell_read(cell, &len);
-+ if (IS_ERR(buf))
-+ return PTR_ERR(buf);
-+
-+ if (ar->hw_params.cal_data_len != len) {
-+ kfree(buf);
-+ ath10k_warn(ar, "invalid calibration data length in nvmem-cell '%s': %zu != %u\n",
-+ cell_name, len, ar->hw_params.cal_data_len);
-+ return -EMSGSIZE;
-+ }
-+
-+ ret = ath10k_download_board_data(ar, buf, len);
-+ kfree(buf);
-+ if (ret)
-+ ath10k_warn(ar, "failed to download calibration data from nvmem-cell '%s': %d\n",
-+ cell_name, ret);
-+
-+ return ret;
-+}
-+
- int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name,
- struct ath10k_fw_file *fw_file)
- {
-@@ -2118,6 +2154,18 @@ static int ath10k_core_pre_cal_download(
- {
- int ret;
-
-+ ret = ath10k_download_cal_nvmem(ar, "pre-calibration");
-+ if (ret == 0) {
-+ ar->cal_mode = ATH10K_PRE_CAL_MODE_NVMEM;
-+ goto success;
-+ } else if (ret == -EPROBE_DEFER) {
-+ return ret;
-+ }
-+
-+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
-+ "boot did not find a pre-calibration nvmem-cell, try file next: %d\n",
-+ ret);
-+
- ret = ath10k_download_cal_file(ar, ar->pre_cal_file);
- if (ret == 0) {
- ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE;
-@@ -2184,6 +2232,18 @@ static int ath10k_download_cal_data(stru
- "pre cal download procedure failed, try cal file: %d\n",
- ret);
-
-+ ret = ath10k_download_cal_nvmem(ar, "calibration");
-+ if (ret == 0) {
-+ ar->cal_mode = ATH10K_CAL_MODE_NVMEM;
-+ goto done;
-+ } else if (ret == -EPROBE_DEFER) {
-+ return ret;
-+ }
-+
-+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
-+ "boot did not find a calibration nvmem-cell, try file next: %d\n",
-+ ret);
-+
- ret = ath10k_download_cal_file(ar, ar->cal_file);
- if (ret == 0) {
- ar->cal_mode = ATH10K_CAL_MODE_FILE;
---- a/drivers/net/wireless/ath/ath10k/core.h
-+++ b/drivers/net/wireless/ath/ath10k/core.h
-@@ -877,8 +877,10 @@ enum ath10k_cal_mode {
- ATH10K_CAL_MODE_FILE,
- ATH10K_CAL_MODE_OTP,
- ATH10K_CAL_MODE_DT,
-+ ATH10K_CAL_MODE_NVMEM,
- ATH10K_PRE_CAL_MODE_FILE,
- ATH10K_PRE_CAL_MODE_DT,
-+ ATH10K_PRE_CAL_MODE_NVMEM,
- ATH10K_CAL_MODE_EEPROM,
- };
-
-@@ -898,10 +900,14 @@ static inline const char *ath10k_cal_mod
- return "otp";
- case ATH10K_CAL_MODE_DT:
- return "dt";
-+ case ATH10K_CAL_MODE_NVMEM:
-+ return "nvmem";
- case ATH10K_PRE_CAL_MODE_FILE:
- return "pre-cal-file";
- case ATH10K_PRE_CAL_MODE_DT:
- return "pre-cal-dt";
-+ case ATH10K_PRE_CAL_MODE_NVMEM:
-+ return "pre-cal-nvmem";
- case ATH10K_CAL_MODE_EEPROM:
- return "eeprom";
- }
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -3443,6 +3443,16 @@ int ath10k_core_register(struct ath10k *
+@@ -3500,6 +3500,16 @@ int ath10k_core_register(struct ath10k *
queue_work(ar->workqueue, &ar->register_work);
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
-@@ -9898,6 +9898,21 @@ static int ath10k_mac_init_rd(struct ath
+@@ -9909,6 +9909,21 @@ static int ath10k_mac_init_rd(struct ath
return 0;
}
int ath10k_mac_register(struct ath10k *ar)
{
static const u32 cipher_suites[] = {
-@@ -10256,6 +10271,12 @@ int ath10k_mac_register(struct ath10k *a
+@@ -10267,6 +10282,12 @@ int ath10k_mac_register(struct ath10k *a
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
--- a/local-symbols
+++ b/local-symbols
-@@ -166,6 +166,7 @@ ATH10K_DEBUG=
+@@ -170,6 +170,7 @@ ATH10K_DEBUG=
ATH10K_DEBUGFS=
ATH10K_SPECTRAL=
ATH10K_THERMAL=
.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
-@@ -138,6 +140,7 @@ static const struct ath10k_hw_params ath
+@@ -144,6 +146,7 @@ static const struct ath10k_hw_params ath
.dev_id = QCA9887_1_0_DEVICE_ID,
.bus = ATH10K_BUS_PCI,
.name = "qca9887 hw1.0",
.patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
-@@ -355,6 +358,7 @@ static const struct ath10k_hw_params ath
+@@ -379,6 +382,7 @@ static const struct ath10k_hw_params ath
.dev_id = QCA99X0_2_0_DEVICE_ID,
.bus = ATH10K_BUS_PCI,
.name = "qca99x0 hw2.0",
.patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
.otp_exe_param = 0x00000700,
-@@ -397,6 +401,7 @@ static const struct ath10k_hw_params ath
+@@ -424,6 +428,7 @@ static const struct ath10k_hw_params ath
.dev_id = QCA9984_1_0_DEVICE_ID,
.bus = ATH10K_BUS_PCI,
.name = "qca9984/qca9994 hw1.0",
.patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
-@@ -446,6 +451,7 @@ static const struct ath10k_hw_params ath
+@@ -476,6 +481,7 @@ static const struct ath10k_hw_params ath
.dev_id = QCA9888_2_0_DEVICE_ID,
.bus = ATH10K_BUS_PCI,
.name = "qca9888 hw2.0",
.patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
.cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
-@@ -3158,6 +3164,10 @@ int ath10k_core_start(struct ath10k *ar,
+@@ -3215,6 +3221,10 @@ int ath10k_core_start(struct ath10k *ar,
goto err_hif_stop;
}
return 0;
err_hif_stop:
-@@ -3416,9 +3426,18 @@ static void ath10k_core_register_work(st
+@@ -3473,9 +3483,18 @@ static void ath10k_core_register_work(st
goto err_spectral_destroy;
}
err_spectral_destroy:
ath10k_spectral_destroy(ar);
err_debug_destroy:
-@@ -3464,6 +3483,8 @@ void ath10k_core_unregister(struct ath10
+@@ -3521,6 +3540,8 @@ void ath10k_core_unregister(struct ath10
if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
return;
#include "htt.h"
#include "htc.h"
-@@ -1256,6 +1257,13 @@ struct ath10k {
+@@ -1253,6 +1254,13 @@ struct ath10k {
} testmode;
struct {
u32 fw_crash_counter;
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
-@@ -517,6 +517,7 @@ struct ath10k_hw_params {
+@@ -519,6 +519,7 @@ struct ath10k_hw_params {
const char *name;
u32 patch_load_addr;
int uart_pin;
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
-@@ -1312,6 +1312,10 @@ struct ath10k {
+@@ -1309,6 +1309,10 @@ struct ath10k {
s32 tx_power_2g_limit;
s32 tx_power_5g_limit;
if (ret)
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
-@@ -10273,7 +10273,7 @@ int ath10k_mac_register(struct ath10k *a
+@@ -10284,7 +10284,7 @@ int ath10k_mac_register(struct ath10k *a
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
#ifdef CPTCFG_MAC80211_LEDS
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
arg.ssid = arvif->u.ap.ssid;
-@@ -3434,7 +3470,8 @@ static int ath10k_update_channel_list(st
+@@ -3437,7 +3473,8 @@ static int ath10k_update_channel_list(st
ch->min_power = 0;
ch->max_power = channel->max_power * 2;
ch->max_reg_power = channel->max_reg_power * 2;
#include <linux/property.h>
#include <linux/dmi.h>
#include <linux/ctype.h>
-@@ -3334,6 +3335,8 @@ static int ath10k_core_probe_fw(struct a
+@@ -3391,6 +3392,8 @@ static int ath10k_core_probe_fw(struct a
- device_get_mac_address(ar->dev, ar->mac_addr, sizeof(ar->mac_addr));
+ device_get_mac_address(ar->dev, ar->mac_addr);
+ of_get_mac_address(ar->dev->of_node, ar->mac_addr);
+
{ AR5K_RXNOFRM, 8 },
--- a/drivers/net/wireless/ath/ath5k/dma.c
+++ b/drivers/net/wireless/ath/ath5k/dma.c
-@@ -869,10 +869,18 @@ ath5k_hw_dma_init(struct ath5k_hw *ah)
+@@ -854,10 +854,18 @@ ath5k_hw_dma_init(struct ath5k_hw *ah)
* guess we can tweak it and see how it goes ;-)
*/
if (ah->ah_version != AR5K_AR5210) {
goto end;
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
-@@ -1963,7 +1963,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
+@@ -2009,7 +2009,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
}
if ((ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs +
ah->opmode == NL80211_IFTYPE_MESH_POINT) {
u64 tsf = ath5k_hw_get_tsf64(ah);
u32 tsftu = TSF_TO_TU(tsf);
-@@ -2049,7 +2049,7 @@ ath5k_beacon_update_timers(struct ath5k_
+@@ -2095,7 +2095,7 @@ ath5k_beacon_update_timers(struct ath5k_
intval = ah->bintval & AR5K_BEACON_PERIOD;
if (ah->opmode == NL80211_IFTYPE_AP && ah->num_ap_vifs
intval /= ATH_BCBUF; /* staggered multi-bss beacons */
if (intval < 15)
ATH5K_WARN(ah, "intval %u is too low, min 15\n",
-@@ -2515,6 +2515,7 @@ static const struct ieee80211_iface_limi
+@@ -2561,6 +2561,7 @@ static const struct ieee80211_iface_limi
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_AP) },
+++ /dev/null
-From 03469e79fee9e8e908dae3bd1a80bcd9a66f2a88 Mon Sep 17 00:00:00 2001
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Mon, 11 Oct 2021 18:18:00 +0300
-Subject: ath9k: support DT ieee80211-freq-limit property to limit channels
-
-The common DT property can be used to limit the available channels
-but ath9k has to manually call wiphy_read_of_freq_limits().
-
-I would have put this into ath9k_of_init(). But it didn't work there.
-The reason is that in ath9k_of_init() the channels and bands are not yet
-registered in the wiphy struct. So there isn't any channel to flag as
-disabled.
-
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-Link: https://lore.kernel.org/r/20211009212847.1781986-1-chunkeey@gmail.com
----
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -1038,6 +1038,8 @@ int ath9k_init_device(u16 devid, struct
- ARRAY_SIZE(ath9k_tpt_blink));
- #endif
-
-+ wiphy_read_of_freq_limits(hw->wiphy);
-+
- /* Register with mac80211 */
- error = ieee80211_register_hw(hw);
- if (error)
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -47,7 +47,7 @@ int ath9k_modparam_nohwcrypt;
+@@ -48,7 +48,7 @@ int ath9k_modparam_nohwcrypt;
module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -826,6 +826,7 @@ static const struct ieee80211_iface_limi
+@@ -882,6 +882,7 @@ static const struct ieee80211_iface_limi
BIT(NL80211_IFTYPE_AP) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) },
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -907,6 +907,7 @@ static void ath9k_set_hw_capab(struct at
+@@ -963,6 +963,7 @@ static void ath9k_set_hw_capab(struct at
ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
if (ath9k_ps_enable)
ieee80211_hw_set(hw, SUPPORTS_PS);
-@@ -919,9 +920,6 @@ static void ath9k_set_hw_capab(struct at
+@@ -975,9 +976,6 @@ static void ath9k_set_hw_capab(struct at
IEEE80211_RADIOTAP_MCS_HAVE_STBC;
}
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1364,6 +1364,53 @@ void ath9k_deinit_debug(struct ath_softc
+@@ -1413,6 +1413,53 @@ void ath9k_deinit_debug(struct ath_softc
ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
}
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
-@@ -1383,6 +1430,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1432,6 +1479,8 @@ int ath9k_init_debug(struct ath_hw *ah)
ath9k_tx99_init_debug(sc);
ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -1122,25 +1122,25 @@ static int __init ath9k_init(void)
+@@ -1178,25 +1178,25 @@ static int __init ath9k_init(void)
{
int error;
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1411,6 +1411,52 @@ static const struct file_operations fops
+@@ -1460,6 +1460,52 @@ static const struct file_operations fops
.owner = THIS_MODULE
};
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
-@@ -1432,6 +1478,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1481,6 +1527,8 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_eeprom);
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -1032,7 +1032,7 @@ int ath9k_init_device(u16 devid, struct
+@@ -1088,7 +1088,7 @@ int ath9k_init_device(u16 devid, struct
#ifdef CPTCFG_MAC80211_LEDS
/* must be initialized before ieee80211_register_hw */
#endif
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1456,6 +1456,61 @@ static const struct file_operations fops
+@@ -1505,6 +1505,61 @@ static const struct file_operations fops
.llseek = default_llseek,
};
int ath9k_init_debug(struct ath_hw *ah)
{
-@@ -1480,6 +1535,10 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1529,6 +1584,10 @@ int ath9k_init_debug(struct ath_hw *ah)
&fops_eeprom);
debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_chanbw);
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1512,6 +1512,50 @@ static const struct file_operations fops
+@@ -1561,6 +1561,50 @@ static const struct file_operations fops
#endif
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
-@@ -1539,6 +1583,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1588,6 +1632,8 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("gpio_led", S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_gpio_led);
#endif
bool reset_power_on;
bool htc_reset_init;
-@@ -1077,6 +1085,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
+@@ -1079,6 +1087,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
bool ath9k_hw_check_alive(struct ath_hw *ah);
bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
void (*spectral_scan_trigger)(struct ath_hw *ah);
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
-@@ -1927,6 +1927,26 @@ void ar9003_hw_init_rate_txpower(struct
+@@ -1918,6 +1918,26 @@ void ar9003_hw_init_rate_txpower(struct
}
}
void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
-@@ -1963,6 +1983,7 @@ void ar9003_hw_attach_phy_ops(struct ath
+@@ -1954,6 +1974,7 @@ void ar9003_hw_attach_phy_ops(struct ath
priv_ops->set_radar_params = ar9003_hw_set_radar_params;
priv_ops->fast_chan_change = ar9003_hw_fast_chan_change;
ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -814,7 +814,8 @@ static void ath9k_init_txpower_limits(st
+@@ -870,7 +870,8 @@ static void ath9k_init_txpower_limits(st
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
}
static const struct ieee80211_iface_limit if_limits[] = {
-@@ -992,6 +993,18 @@ static void ath9k_set_hw_capab(struct at
+@@ -1048,6 +1049,18 @@ static void ath9k_set_hw_capab(struct at
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
}
int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
-@@ -1039,6 +1052,8 @@ int ath9k_init_device(u16 devid, struct
+@@ -1095,6 +1108,8 @@ int ath9k_init_device(u16 devid, struct
wiphy_read_of_freq_limits(hw->wiphy);
static const u8 ofdm2pwr[] = {
ALL_TARGET_LEGACY_6_24,
ALL_TARGET_LEGACY_6_24,
-@@ -1077,11 +1063,6 @@ static bool ar9003_hw_ani_control(struct
+@@ -1068,11 +1054,6 @@ static bool ar9003_hw_ani_control(struct
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
struct ar5416AniState *aniState = &ah->ani;
s32 value, value2;
switch (cmd & ah->ani_function) {
-@@ -1095,61 +1076,6 @@ static bool ar9003_hw_ani_control(struct
+@@ -1086,61 +1067,6 @@ static bool ar9003_hw_ani_control(struct
*/
u32 on = param ? 1 : 0;
--- a/local-symbols
+++ b/local-symbols
-@@ -133,6 +133,7 @@ ATH9K_WOW=
+@@ -137,6 +137,7 @@ ATH9K_WOW=
ATH9K_RFKILL=
ATH9K_CHANNEL_CONTEXT=
ATH9K_PCOEM=
if (!dev_get_platdata(&pdev->dev)) {
dev_err(&pdev->dev, "no platform data specified\n");
-@@ -122,13 +371,16 @@ static int ath_ahb_probe(struct platform
+@@ -118,13 +367,16 @@ static int ath_ahb_probe(struct platform
sc->mem = mem;
sc->irq = irq;
if (ret) {
dev_err(&pdev->dev, "failed to initialize device\n");
goto err_irq;
-@@ -159,6 +411,9 @@ static int ath_ahb_remove(struct platfor
+@@ -155,6 +407,9 @@ static int ath_ahb_remove(struct platfor
free_irq(sc->irq, sc);
ieee80211_free_hw(sc->hw);
}
return 0;
}
-@@ -168,6 +423,9 @@ static struct platform_driver ath_ahb_dr
+@@ -164,6 +419,9 @@ static struct platform_driver ath_ahb_dr
.remove = ath_ahb_remove,
.driver = {
.name = "ath9k",
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -644,6 +644,12 @@ static int ath9k_of_init(struct ath_soft
+@@ -696,6 +696,12 @@ static int ath9k_of_init(struct ath_soft
return 0;
}
static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops)
{
-@@ -747,6 +753,9 @@ static int ath9k_init_softc(u16 devid, s
+@@ -803,6 +809,9 @@ static int ath9k_init_softc(u16 devid, s
if (ret)
goto err_hw;
+++ /dev/null
-From dab16ef495dbb3cabb355b6c80f0771a4a25e35d Mon Sep 17 00:00:00 2001
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Fri, 20 Aug 2021 22:44:52 +0200
-Subject: [PATCH] ath9k: fetch calibration data via nvmem subsystem
-
-On most embedded ath9k devices (like range extenders,
-routers, accesspoints, ...) the calibration data is
-stored in a MTD partitions named "ART", or "caldata"/
-"calibration".
-
-Ever since commit
-4b361cfa8624 ("mtd: core: add OTP nvmem provider support")
-all MTD partitions are all automatically available through
-the nvmem subsystem. This allows drivers like ath9k to read
-the necessary data without needing any userspace helpers
-that would do this extraction.
-
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
----
-
-includes:
-
-From 57671351379b2051cfb07fc14e0bead9916a0880 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Mon, 11 Oct 2021 18:18:01 +0300
-Subject: ath9k: fix an IS_ERR() vs NULL check
-
-The devm_kmemdup() function doesn't return error pointers, it returns
-NULL on error.
-
-Fixes: eb3a97a69be8 ("ath9k: fetch calibration data via nvmem subsystem")
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-Link: https://lore.kernel.org/r/20211011123533.GA15188@kili
-
----
-
---- a/drivers/net/wireless/ath/ath9k/eeprom.c
-+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
-@@ -135,13 +135,23 @@ static bool ath9k_hw_nvram_read_firmware
- offset, data);
- }
-
-+static bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset,
-+ u16 *data)
-+{
-+ return ath9k_hw_nvram_read_array(ah->nvmem_blob,
-+ ah->nvmem_blob_len / sizeof(u16),
-+ offset, data);
-+}
-+
- bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
- {
- struct ath_common *common = ath9k_hw_common(ah);
- struct ath9k_platform_data *pdata = ah->dev->platform_data;
- bool ret;
-
-- if (ah->eeprom_blob)
-+ if (ah->nvmem_blob)
-+ ret = ath9k_hw_nvram_read_nvmem(ah, off, data);
-+ else if (ah->eeprom_blob)
- ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data);
- else if (pdata && !pdata->use_eeprom)
- ret = ath9k_hw_nvram_read_pdata(pdata, off, data);
---- a/drivers/net/wireless/ath/ath9k/hw.h
-+++ b/drivers/net/wireless/ath/ath9k/hw.h
-@@ -988,6 +988,8 @@ struct ath_hw {
- bool disable_5ghz;
-
- const struct firmware *eeprom_blob;
-+ u16 *nvmem_blob; /* devres managed */
-+ size_t nvmem_blob_len;
-
- struct ath_dynack dynack;
-
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -22,6 +22,7 @@
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_net.h>
-+#include <linux/nvmem-consumer.h>
- #include <linux/relay.h>
- #include <linux/dmi.h>
- #include <net/ieee80211_radiotap.h>
-@@ -568,6 +569,57 @@ static void ath9k_eeprom_release(struct
- release_firmware(sc->sc_ah->eeprom_blob);
- }
-
-+static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
-+{
-+ struct ath_hw *ah = sc->sc_ah;
-+ struct nvmem_cell *cell;
-+ void *buf;
-+ size_t len;
-+ int err;
-+
-+ cell = devm_nvmem_cell_get(sc->dev, "calibration");
-+ if (IS_ERR(cell)) {
-+ err = PTR_ERR(cell);
-+
-+ /* nvmem cell might not be defined, or the nvmem
-+ * subsystem isn't included. In this case, follow
-+ * the established "just return 0;" convention of
-+ * ath9k_init_platform to say:
-+ * "All good. Nothing to see here. Please go on."
-+ */
-+ if (err == -ENOENT || err == -EOPNOTSUPP)
-+ return 0;
-+
-+ return err;
-+ }
-+
-+ buf = nvmem_cell_read(cell, &len);
-+ if (IS_ERR(buf))
-+ return PTR_ERR(buf);
-+
-+ /* run basic sanity checks on the returned nvram cell length.
-+ * That length has to be a multiple of a "u16" (i.e.: & 1).
-+ * Furthermore, it has to be more than "let's say" 512 bytes
-+ * but less than the maximum of AR9300_EEPROM_SIZE (16kb).
-+ */
-+ if (((len & 1) == 1) || (len < 512) || (len >= AR9300_EEPROM_SIZE)) {
-+ kfree(buf);
-+ return -EINVAL;
-+ }
-+
-+ /* devres manages the calibration values release on shutdown */
-+ ah->nvmem_blob = (u16 *)devm_kmemdup(sc->dev, buf, len, GFP_KERNEL);
-+ kfree(buf);
-+ if (!ah->nvmem_blob)
-+ return -ENOMEM;
-+
-+ ah->nvmem_blob_len = len;
-+ ah->ah_flags &= ~AH_USE_EEPROM;
-+ ah->ah_flags |= AH_NO_EEP_SWAP;
-+
-+ return 0;
-+}
-+
- static int ath9k_init_platform(struct ath_softc *sc)
- {
- struct ath9k_platform_data *pdata = sc->dev->platform_data;
-@@ -710,6 +762,10 @@ static int ath9k_init_softc(u16 devid, s
- if (ret)
- return ret;
-
-+ ret = ath9k_nvmem_request_eeprom(sc);
-+ if (ret)
-+ return ret;
-+
- if (ath9k_led_active_high != -1)
- ah->config.led_active_high = ath9k_led_active_high == 1;
-
+++ /dev/null
-From 9bf31835f11aa3c4fe5a9c1f7462c199c5d8e7ca Mon Sep 17 00:00:00 2001
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Sat, 21 Aug 2021 00:22:39 +0200
-Subject: [PATCH] ath9k: owl-loader: fetch pci init values through nvmem
-
-extends the owl loader to fetch important pci initialization
-values - which are stored together with the calibration data -
-through the nvmem subsystem.
-
-This allows for much faster WIFI/ath9k initializations on devices
-that do not require to perform any post-processing (like XOR'ing/
-reversal or unpacking) since no userspace helper is required.
-
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
----
- .../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 105 +++++++++++++-----
- 1 file changed, 76 insertions(+), 29 deletions(-)
-
---- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
-+++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
-@@ -19,9 +19,14 @@
- #include <linux/delay.h>
- #include <linux/platform_device.h>
- #include <linux/ath9k_platform.h>
-+#include <linux/nvmem-consumer.h>
-+#include <linux/workqueue.h>
-
- struct owl_ctx {
-+ struct pci_dev *pdev;
- struct completion eeprom_load;
-+ struct work_struct work;
-+ struct nvmem_cell *cell;
- };
-
- #define EEPROM_FILENAME_LEN 100
-@@ -42,6 +47,12 @@ static int ath9k_pci_fixup(struct pci_de
- u32 bar0;
- bool swap_needed = false;
-
-+ /* also note that we are doing *u16 operations on the file */
-+ if (cal_len > 4096 || cal_len < 0x200 || (cal_len & 1) == 1) {
-+ dev_err(&pdev->dev, "eeprom has an invalid size.\n");
-+ return -EINVAL;
-+ }
-+
- if (*cal_data != AR5416_EEPROM_MAGIC) {
- if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
- dev_err(&pdev->dev, "invalid calibration data\n");
-@@ -99,38 +110,31 @@ static int ath9k_pci_fixup(struct pci_de
- return 0;
- }
-
--static void owl_fw_cb(const struct firmware *fw, void *context)
-+static void owl_rescan(struct pci_dev *pdev)
- {
-- struct pci_dev *pdev = (struct pci_dev *)context;
-- struct owl_ctx *ctx = (struct owl_ctx *)pci_get_drvdata(pdev);
-- struct pci_bus *bus;
--
-- complete(&ctx->eeprom_load);
--
-- if (!fw) {
-- dev_err(&pdev->dev, "no eeprom data received.\n");
-- goto release;
-- }
--
-- /* also note that we are doing *u16 operations on the file */
-- if (fw->size > 4096 || fw->size < 0x200 || (fw->size & 1) == 1) {
-- dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
-- goto release;
-- }
--
-- if (ath9k_pci_fixup(pdev, (const u16 *)fw->data, fw->size))
-- goto release;
-+ struct pci_bus *bus = pdev->bus;
-
- pci_lock_rescan_remove();
-- bus = pdev->bus;
- pci_stop_and_remove_bus_device(pdev);
- /* the device should come back with the proper
- * ProductId. But we have to initiate a rescan.
- */
- pci_rescan_bus(bus);
- pci_unlock_rescan_remove();
-+}
-+
-+static void owl_fw_cb(const struct firmware *fw, void *context)
-+{
-+ struct owl_ctx *ctx = (struct owl_ctx *)context;
-+
-+ complete(&ctx->eeprom_load);
-
--release:
-+ if (fw) {
-+ ath9k_pci_fixup(ctx->pdev, (const u16 *)fw->data, fw->size);
-+ owl_rescan(ctx->pdev);
-+ } else {
-+ dev_err(&ctx->pdev->dev, "no eeprom data received.\n");
-+ }
- release_firmware(fw);
- }
-
-@@ -152,6 +156,43 @@ static const char *owl_get_eeprom_name(s
- return eeprom_name;
- }
-
-+static void owl_nvmem_work(struct work_struct *work)
-+{
-+ struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
-+ void *buf;
-+ size_t len;
-+
-+ complete(&ctx->eeprom_load);
-+
-+ buf = nvmem_cell_read(ctx->cell, &len);
-+ if (!IS_ERR(buf)) {
-+ ath9k_pci_fixup(ctx->pdev, buf, len);
-+ kfree(buf);
-+ owl_rescan(ctx->pdev);
-+ } else {
-+ dev_err(&ctx->pdev->dev, "no nvmem data received.\n");
-+ }
-+}
-+
-+static int owl_nvmem_probe(struct owl_ctx *ctx)
-+{
-+ int err;
-+
-+ ctx->cell = devm_nvmem_cell_get(&ctx->pdev->dev, "calibration");
-+ if (IS_ERR(ctx->cell)) {
-+ err = PTR_ERR(ctx->cell);
-+ if (err == -ENOENT || err == -EOPNOTSUPP)
-+ return 1; /* not present, try firmware_request */
-+
-+ return err;
-+ }
-+
-+ INIT_WORK(&ctx->work, owl_nvmem_work);
-+ schedule_work(&ctx->work);
-+
-+ return 0;
-+}
-+
- static int owl_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
- {
-@@ -164,21 +205,27 @@ static int owl_probe(struct pci_dev *pde
-
- pcim_pin_device(pdev);
-
-- eeprom_name = owl_get_eeprom_name(pdev);
-- if (!eeprom_name) {
-- dev_err(&pdev->dev, "no eeprom filename found.\n");
-- return -ENODEV;
-- }
--
- ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- init_completion(&ctx->eeprom_load);
-+ ctx->pdev = pdev;
-
- pci_set_drvdata(pdev, ctx);
-+
-+ err = owl_nvmem_probe(ctx);
-+ if (err <= 0)
-+ return err;
-+
-+ eeprom_name = owl_get_eeprom_name(pdev);
-+ if (!eeprom_name) {
-+ dev_err(&pdev->dev, "no eeprom filename found.\n");
-+ return -ENODEV;
-+ }
-+
- err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
-- &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
-+ &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
- if (err)
- dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
-
+++ /dev/null
-From 716c220b4d990a4fe7800d0685ca69dee99e4e8f Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Pavel=20L=C3=B6bl?= <pavel@loebl.cz>
-Date: Fri, 6 May 2022 06:42:46 +0200
-Subject: [PATCH] brcmfmac: allow setting wlan MAC address using device tree
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This allows firmware to provide MAC address using device tree. Like in
-case there is no MAC burned in wlan NVRAM.
-
-Signed-off-by: Pavel Löbl <pavel@loebl.cz>
-Signed-off-by: Kalle Valo <kvalo@kernel.org>
-Link: https://lore.kernel.org/r/20220506044246.67146-1-pavel@loebl.cz
----
- .../broadcom/brcm80211/brcmfmac/common.c | 23 ++++++++++++++-----
- .../broadcom/brcm80211/brcmfmac/common.h | 1 +
- .../broadcom/brcm80211/brcmfmac/core.c | 4 +++-
- .../wireless/broadcom/brcm80211/brcmfmac/of.c | 3 +++
- 4 files changed, 24 insertions(+), 7 deletions(-)
-
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
-@@ -202,13 +202,24 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
- char *ptr;
- s32 err;
-
-- /* retreive mac address */
-- err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
-- sizeof(ifp->mac_addr));
-- if (err < 0) {
-- bphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err);
-- goto done;
-+ if (is_valid_ether_addr(ifp->mac_addr)) {
-+ /* set mac address */
-+ err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
-+ ETH_ALEN);
-+ if (err < 0) {
-+ bphy_err(ifp->drvr, "Setting cur_etheraddr failed, %d\n", err);
-+ goto done;
-+ }
-+ } else {
-+ /* retrieve mac address */
-+ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
-+ sizeof(ifp->mac_addr));
-+ if (err < 0) {
-+ bphy_err(drvr, "Retrieving cur_etheraddr failed, %d\n", err);
-+ goto done;
-+ }
- }
-+
- memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
- memcpy(ifp->drvr->wiphy->perm_addr, ifp->drvr->mac, ETH_ALEN);
-
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
-@@ -50,6 +50,7 @@ struct brcmf_mp_device {
- bool ignore_probe_fail;
- struct brcmfmac_pd_cc *country_codes;
- const char *board_type;
-+ unsigned char mac[ETH_ALEN];
- union {
- struct brcmfmac_sdio_pd sdio;
- } bus;
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-@@ -7,6 +7,7 @@
- #include <linux/etherdevice.h>
- #include <linux/module.h>
- #include <linux/inetdevice.h>
-+#include <linux/property.h>
- #include <net/cfg80211.h>
- #include <net/rtnetlink.h>
- #include <net/addrconf.h>
-@@ -1227,7 +1228,8 @@ static int brcmf_bus_started(struct brcm
- brcmf_dbg(TRACE, "\n");
-
- /* add primary networking interface */
-- ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
-+ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d",
-+ is_valid_ether_addr(drvr->settings->mac) ? drvr->settings->mac : NULL);
- if (IS_ERR(ifp))
- return PTR_ERR(ifp);
-
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
-@@ -5,6 +5,7 @@
- #include <linux/init.h>
- #include <linux/of.h>
- #include <linux/of_irq.h>
-+#include <linux/of_net.h>
-
- #include <defs.h>
- #include "debug.h"
-@@ -97,6 +98,8 @@ void brcmf_of_probe(struct device *dev,
- if (err)
- brcmf_err("failed to get OF country code map (err=%d)\n", err);
-
-+ of_get_mac_address(np, settings->mac);
-+
- if (bus_type != BRCMF_BUSTYPE_SDIO)
- return;
-
if (phy->type == B43_PHYTYPE_B) {
value16 = b43_read16(dev, 0x005E);
-@@ -3985,7 +3985,6 @@ static int b43_op_config(struct ieee8021
+@@ -3986,7 +3986,6 @@ static int b43_op_config(struct ieee8021
struct b43_wldev *dev = wl->current_dev;
struct b43_phy *phy = &dev->phy;
struct ieee80211_conf *conf = &hw->conf;
int err = 0;
mutex_lock(&wl->mutex);
-@@ -4028,11 +4027,9 @@ static int b43_op_config(struct ieee8021
+@@ -4029,11 +4028,9 @@ static int b43_op_config(struct ieee8021
}
/* Antennas for RX and management frame TX. */
if (wl->radio_enabled != phy->radio_on) {
if (wl->radio_enabled) {
-@@ -5175,6 +5172,47 @@ static int b43_op_get_survey(struct ieee
+@@ -5176,6 +5173,47 @@ static int b43_op_get_survey(struct ieee
return 0;
}
+
static const struct ieee80211_ops b43_hw_ops = {
.tx = b43_op_tx,
- .conf_tx = b43_op_conf_tx,
-@@ -5196,6 +5234,8 @@ static const struct ieee80211_ops b43_hw
+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+@@ -5198,6 +5236,8 @@ static const struct ieee80211_ops b43_hw
.sw_scan_complete = b43_op_sw_scan_complete_notifier,
.get_survey = b43_op_get_survey,
.rfkill_poll = b43_rfkill_poll,
};
/* Hard-reset the chip. Do not call this directly.
-@@ -5497,6 +5537,8 @@ static int b43_one_core_attach(struct b4
+@@ -5499,6 +5539,8 @@ static int b43_one_core_attach(struct b4
if (!wldev)
goto out;
wldev->use_pio = b43_modparam_pio;
wldev->dev = dev;
wldev->wl = wl;
-@@ -5588,6 +5630,9 @@ static struct b43_wl *b43_wireless_init(
+@@ -5590,6 +5632,9 @@ static struct b43_wl *b43_wireless_init(
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
-@@ -114,7 +114,7 @@ static int b43_modparam_pio = 0;
+@@ -114,7 +114,7 @@ static int b43_modparam_pio;
module_param_named(pio, b43_modparam_pio, int, 0644);
MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO");
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
-@@ -431,6 +431,7 @@ struct brcmf_fw {
- struct brcmf_fw_request *req;
+@@ -459,6 +459,7 @@ struct brcmf_fw {
u32 curpos;
+ unsigned int board_index;
void (*done)(struct device *dev, int err, struct brcmf_fw_request *req);
+ struct completion *completion;
};
#ifdef CONFIG_EFI
-@@ -655,6 +656,8 @@ static void brcmf_fw_request_done(const
+@@ -686,6 +687,8 @@ static void brcmf_fw_request_done(const
fwctx->req = NULL;
}
fwctx->done(fwctx->dev, ret, fwctx->req);
kfree(fwctx);
}
-@@ -695,6 +698,8 @@ int brcmf_fw_get_firmwares(struct device
+@@ -751,6 +754,8 @@ int brcmf_fw_get_firmwares(struct device
{
struct brcmf_fw_item *first = &req->items[0];
struct brcmf_fw *fwctx;
char *alt_path = NULL;
int ret;
-@@ -712,6 +717,9 @@ int brcmf_fw_get_firmwares(struct device
+@@ -768,6 +773,9 @@ int brcmf_fw_get_firmwares(struct device
fwctx->dev = dev;
fwctx->req = req;
fwctx->done = fw_cb;
+ fwctx->completion = &completion;
/* First try alternative board-specific path if any */
- if (fwctx->req->board_type)
-@@ -730,6 +738,12 @@ int brcmf_fw_get_firmwares(struct device
+ if (fwctx->req->board_types[0])
+@@ -787,6 +795,12 @@ int brcmf_fw_get_firmwares(struct device
if (ret < 0)
brcmf_fw_request_done(NULL, fwctx);
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-@@ -715,8 +715,36 @@ static struct wireless_dev *brcmf_cfg802
+@@ -710,8 +710,36 @@ static struct wireless_dev *brcmf_cfg802
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_pub *drvr = cfg->pub;
struct wireless_dev *wdev;
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-@@ -2974,6 +2974,10 @@ brcmf_cfg80211_set_power_mgmt(struct wip
+@@ -2973,6 +2973,10 @@ brcmf_cfg80211_set_power_mgmt(struct wip
* preference in cfg struct to apply this to
* FW later while initializing the dongle
*/
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
-@@ -59,6 +59,36 @@ static int brcmf_of_get_country_codes(st
+@@ -65,6 +65,36 @@ static int brcmf_of_get_country_codes(st
return 0;
}
void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
struct brcmf_mp_device *settings)
{
-@@ -91,6 +121,8 @@ void brcmf_of_probe(struct device *dev,
+@@ -105,6 +135,8 @@ void brcmf_of_probe(struct device *dev,
of_node_put(root);
}
#include "of.h"
static int brcmf_of_get_country_codes(struct device *dev,
-@@ -153,3 +154,38 @@ void brcmf_of_probe(struct device *dev,
+@@ -167,3 +168,38 @@ void brcmf_of_probe(struct device *dev,
sdio->oob_irq_nr = irq;
sdio->oob_irq_flags = irqf;
}
#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
-@@ -633,7 +634,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "b
+@@ -634,7 +635,7 @@ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "b
/* per-board firmware binaries */
MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH "brcmfmac*-sdio.*.bin");
BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
BRCMF_FW_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
-@@ -659,6 +660,9 @@ static const struct brcmf_firmware_mappi
+@@ -662,6 +663,9 @@ static const struct brcmf_firmware_mappi
BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752)
};
#define TXCTL_CREDITS 2
static void pkt_align(struct sk_buff *p, int len, int align)
-@@ -4140,7 +4144,7 @@ int brcmf_sdio_get_fwname(struct device
-
- fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev,
- brcmf_sdio_fwnames,
-- ARRAY_SIZE(brcmf_sdio_fwnames),
-+ brcmf_sdio_fwnames_count,
- fwnames, ARRAY_SIZE(fwnames));
- if (!fwreq)
- return -ENOMEM;
-@@ -4196,6 +4200,9 @@ static const struct brcmf_bus_ops brcmf_
- #define BRCMF_SDIO_FW_CODE 0
+@@ -4192,6 +4196,9 @@ static const struct brcmf_bus_ops brcmf_
#define BRCMF_SDIO_FW_NVRAM 1
+ #define BRCMF_SDIO_FW_CLM 2
+static struct brcmf_fw_request *
+brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus);
static void brcmf_sdio_firmware_callback(struct device *dev, int err,
struct brcmf_fw_request *fwreq)
{
-@@ -4211,6 +4218,22 @@ static void brcmf_sdio_firmware_callback
+@@ -4207,6 +4214,22 @@ static void brcmf_sdio_firmware_callback
brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
if (err)
goto fail;
-@@ -4419,7 +4442,7 @@ brcmf_sdio_prepare_fw_request(struct brc
+@@ -4417,7 +4440,7 @@ brcmf_sdio_prepare_fw_request(struct brc
fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev,
brcmf_sdio_fwnames,
+++ /dev/null
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
-@@ -2921,6 +2921,63 @@ done:
- }
-
- static int
-+brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
-+ int idx, struct survey_info *survey)
-+{
-+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-+ struct brcmf_if *ifp = netdev_priv(ndev);
-+ struct brcmu_chan ch;
-+ enum nl80211_band band = 0;
-+ s32 err = 0;
-+ int noise;
-+ u32 freq;
-+ u32 chanspec;
-+
-+ memset(survey, 0, sizeof(struct survey_info));
-+ if (idx != 0) {
-+ if (idx >= cfg->pub->num_chan_stats || cfg->pub->chan_stats == NULL)
-+ return -ENOENT;
-+ if (cfg->pub->chan_stats[idx].freq == 0)
-+ return -ENOENT;
-+ survey->filled = SURVEY_INFO_NOISE_DBM;
-+ survey->channel = ieee80211_get_channel(wiphy, cfg->pub->chan_stats[idx].freq);
-+ survey->noise = cfg->pub->chan_stats[idx].noise;
-+ return 0;
-+ }
-+
-+ err = brcmf_fil_iovar_int_get(ifp, "chanspec", &chanspec);
-+ if (err) {
-+ brcmf_err("chanspec failed (%d)\n", err);
-+ return err;
-+ }
-+
-+ ch.chspec = chanspec;
-+ cfg->d11inf.decchspec(&ch);
-+
-+ switch (ch.band) {
-+ case BRCMU_CHAN_BAND_2G:
-+ band = NL80211_BAND_2GHZ;
-+ break;
-+ case BRCMU_CHAN_BAND_5G:
-+ band = NL80211_BAND_5GHZ;
-+ break;
-+ }
-+
-+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
-+ survey->channel = ieee80211_get_channel(wiphy, freq);
-+
-+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);
-+ if (err) {
-+ brcmf_err("Could not get noise (%d)\n", err);
-+ return err;
-+ }
-+
-+ survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_IN_USE;
-+ survey->noise = le32_to_cpu(noise);
-+ return 0;
-+}
-+
-+static int
- brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
- int idx, u8 *mac, struct station_info *sinfo)
- {
-@@ -3021,6 +3078,7 @@ static s32 brcmf_inform_single_bss(struc
- struct brcmu_chan ch;
- u16 channel;
- u32 freq;
-+ int i;
- u16 notify_capability;
- u16 notify_interval;
- u8 *notify_ie;
-@@ -3045,6 +3103,17 @@ static s32 brcmf_inform_single_bss(struc
- band = NL80211_BAND_5GHZ;
-
- freq = ieee80211_channel_to_frequency(channel, band);
-+ for (i = 0;i < cfg->pub->num_chan_stats;i++) {
-+ if (freq == cfg->pub->chan_stats[i].freq)
-+ break;
-+ if (cfg->pub->chan_stats[i].freq == 0)
-+ break;
-+ }
-+ if (i < cfg->pub->num_chan_stats) {
-+ cfg->pub->chan_stats[i].freq = freq;
-+ cfg->pub->chan_stats[i].noise = bi->phy_noise;
-+ }
-+
- bss_data.chan = ieee80211_get_channel(wiphy, freq);
- bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
- bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
-@@ -5573,6 +5642,7 @@ static struct cfg80211_ops brcmf_cfg8021
- .leave_ibss = brcmf_cfg80211_leave_ibss,
- .get_station = brcmf_cfg80211_get_station,
- .dump_station = brcmf_cfg80211_dump_station,
-+ .dump_survey = brcmf_cfg80211_dump_survey,
- .set_tx_power = brcmf_cfg80211_set_tx_power,
- .get_tx_power = brcmf_cfg80211_get_tx_power,
- .add_key = brcmf_cfg80211_add_key,
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-@@ -1364,6 +1364,8 @@ int brcmf_attach(struct device *dev)
-
- /* Link to bus module */
- drvr->hdrlen = 0;
-+ drvr->chan_stats = vzalloc(256 * sizeof(struct brcmf_chan_stats));
-+ drvr->num_chan_stats = 256;
-
- /* Attach and link in the protocol */
- ret = brcmf_proto_attach(drvr);
-@@ -1446,6 +1448,12 @@ void brcmf_detach(struct device *dev)
- if (drvr == NULL)
- return;
-
-+ drvr->num_chan_stats = 0;
-+ if (drvr->chan_stats) {
-+ vfree(drvr->chan_stats);
-+ drvr->chan_stats = NULL;
-+ }
-+
- #ifdef CONFIG_INET
- unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
- #endif
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
-@@ -91,6 +91,11 @@ struct brcmf_rev_info {
- u32 nvramrev;
- };
-
-+struct brcmf_chan_stats {
-+ u32 freq;
-+ int noise;
-+};
-+
- /* Common structure for module and instance linkage */
- struct brcmf_pub {
- /* Linkage ponters */
-@@ -100,6 +105,9 @@ struct brcmf_pub {
- struct cfg80211_ops *ops;
- struct brcmf_cfg80211_info *config;
-
-+ int num_chan_stats;
-+ struct brcmf_chan_stats *chan_stats;
-+
- /* Internal brcmf items */
- uint hdrlen; /* Total BRCMF header length (proto + bus) */
-
+++ /dev/null
---- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
-+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
-@@ -11470,6 +11470,15 @@ static const struct attribute_group ipw_
- .attrs = ipw_sysfs_entries,
- };
-
-+#if LINUX_VERSION_IS_LESS(4,10,0)
-+static int __change_mtu(struct net_device *ndev, int new_mtu){
-+ if (new_mtu < 68 || new_mtu > LIBIPW_DATA_LEN)
-+ return -EINVAL;
-+ ndev->mtu = new_mtu;
-+ return 0;
-+}
-+#endif
-+
- #ifdef CPTCFG_IPW2200_PROMISCUOUS
- static int ipw_prom_open(struct net_device *dev)
- {
-@@ -11518,15 +11527,6 @@ static netdev_tx_t ipw_prom_hard_start_x
- return NETDEV_TX_OK;
- }
-
--#if LINUX_VERSION_IS_LESS(4,10,0)
--static int __change_mtu(struct net_device *ndev, int new_mtu){
-- if (new_mtu < 68 || new_mtu > LIBIPW_DATA_LEN)
-- return -EINVAL;
-- ndev->mtu = new_mtu;
-- return 0;
--}
--#endif
--
- static const struct net_device_ops ipw_prom_netdev_ops = {
- #if LINUX_VERSION_IS_LESS(4,10,0)
- .ndo_change_mtu = __change_mtu,
--- a/local-symbols
+++ b/local-symbols
-@@ -451,43 +451,6 @@ USB_VL600=
+@@ -470,43 +470,6 @@ USB_VL600=
USB_NET_CH9200=
USB_NET_AQC111=
USB_RTL8153_ECM=
return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev);
#else
return bus->chipco.dev;
-@@ -4870,7 +4870,7 @@ static int b43_wireless_core_init(struct
+@@ -4871,7 +4871,7 @@ static int b43_wireless_core_init(struct
}
if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
pcidev = bus->pcicore.dev;
#endif
gpiodev = bus->chipco.dev ? : pcidev;
---- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
-@@ -24,7 +24,7 @@ struct brcms_led {
- struct gpio_desc *gpiod;
- };
-
--#ifdef CPTCFG_BCMA_DRIVER_GPIO
-+#ifdef CONFIG_BCMA_DRIVER_GPIO
- void brcms_led_unregister(struct brcms_info *wl);
- int brcms_led_register(struct brcms_info *wl);
- #else
---- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
-@@ -42,6 +42,6 @@ brcmsmac-y := \
- brcms_trace_events.o \
- debug.o
-
--brcmsmac-$(CPTCFG_BCMA_DRIVER_GPIO) += led.o
-+brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o
-
- obj-$(CPTCFG_BRCMSMAC) += brcmsmac.o
--- a/drivers/net/wireless/broadcom/brcm80211/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
@@ -8,7 +8,7 @@ config BRCMSMAC
depends on BCMA_POSSIBLE
- select BCMA
+ depends on BCMA
- select NEW_LEDS if BCMA_DRIVER_GPIO
- select LEDS_CLASS if BCMA_DRIVER_GPIO
select BRCMUTIL
+ depends on FW_LOADER
+ depends on CORDIC
--- a/Kconfig.local
+++ b/Kconfig.local
-@@ -1357,117 +1357,6 @@ config BACKPORTED_USB_NET_AQC111
+@@ -1414,117 +1414,6 @@ config BACKPORTED_USB_NET_AQC111
config BACKPORTED_USB_RTL8153_ECM
tristate
default USB_RTL8153_ECM
depends on WLAN && MMC && CFG80211
depends on m
- select CFG80211_WEXT
+ depends on CRYPTO
select BPAUTO_CRYPTO_LIB_ARC4
help
- This option enables support for RTL8723BS SDIO drivers, such as
--- /dev/null
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -5363,7 +5363,9 @@ static struct genl_family hwsim_genl_fam
+ .module = THIS_MODULE,
+ .small_ops = hwsim_ops,
+ .n_small_ops = ARRAY_SIZE(hwsim_ops),
++#if LINUX_VERSION_IS_GEQ(6,1,0)
+ .resv_start_op = HWSIM_CMD_DEL_MAC_ADDR + 1,
++#endif
+ .mcgrps = hwsim_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
+ };
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -17232,7 +17232,9 @@ static struct genl_family nl80211_fam __
+ .n_ops = ARRAY_SIZE(nl80211_ops),
+ .small_ops = nl80211_small_ops,
+ .n_small_ops = ARRAY_SIZE(nl80211_small_ops),
++#if LINUX_VERSION_IS_GEQ(6,1,0)
+ .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1,
++#endif
+ .mcgrps = nl80211_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps),
+ .parallel_ops = true,
--- /dev/null
+--- /dev/null
++++ b/backport-include/linux/bcma/bcma_driver_chipcommon.h
+@@ -0,0 +1,10 @@
++#ifndef __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
++#define __BACKPORT_BCMA_DRIVER_CHIPCOMMON_H
++
++#include_next <linux/bcma/bcma_driver_chipcommon.h>
++
++#ifndef BCMA_CC_SROM_CONTROL_OTP_PRESENT
++#define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020
++#endif
++
++#endif
--- a/drivers/net/wireless/marvell/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
-@@ -5699,6 +5699,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw")
+@@ -5703,6 +5703,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw")
MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
static const struct pci_device_id mwl8k_pci_id_table[] = {
--- a/drivers/net/wireless/marvell/libertas/cfg.c
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
-@@ -2053,6 +2053,8 @@ struct wireless_dev *lbs_cfg_alloc(struc
+@@ -2052,6 +2052,8 @@ struct wireless_dev *lbs_cfg_alloc(struc
goto err_wiphy_new;
}
err_wiphy_new:
--- a/drivers/net/wireless/marvell/libertas/main.c
+++ b/drivers/net/wireless/marvell/libertas/main.c
-@@ -935,6 +935,7 @@ struct lbs_private *lbs_add_card(void *c
+@@ -934,6 +934,7 @@ struct lbs_private *lbs_add_card(void *c
goto err_adapter;
}
--- a/drivers/net/wireless/marvell/libertas/cfg.c
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
-@@ -2129,6 +2129,8 @@ int lbs_cfg_register(struct lbs_private
+@@ -2128,6 +2128,8 @@ int lbs_cfg_register(struct lbs_private
wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
wdev->wiphy->reg_notifier = lbs_reg_notifier;
--- a/drivers/net/wireless/marvell/mwifiex/decl.h
+++ b/drivers/net/wireless/marvell/mwifiex/decl.h
-@@ -30,7 +30,7 @@
+@@ -18,7 +18,7 @@
#include <net/cfg80211.h>
#define MWIFIEX_BSS_COEX_COUNT 2
#define MWIFIEX_DMA_ALIGN_SZ 64
#define MWIFIEX_RX_HEADROOM 64
-@@ -112,7 +112,7 @@
+@@ -100,7 +100,7 @@
#define MWIFIEX_RATE_INDEX_OFDM0 4
#define MWIFIEX_MAX_STA_NUM 3
--- a/drivers/net/wireless/marvell/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
-@@ -6285,6 +6285,8 @@ static int mwl8k_probe(struct pci_dev *p
+@@ -6289,6 +6289,8 @@ static int mwl8k_probe(struct pci_dev *p
priv->running_bsses = 0;
return rc;
err_stop_firmware:
-@@ -6318,8 +6320,6 @@ static void mwl8k_remove(struct pci_dev
+@@ -6322,8 +6324,6 @@ static void mwl8k_remove(struct pci_dev
return;
priv = hw->priv;
--- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
-@@ -28,6 +28,85 @@
+@@ -16,6 +16,85 @@
static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
/*
* This function initializes a command node.
*
-@@ -205,8 +284,8 @@ static int mwifiex_dnld_cmd_to_fw(struct
+@@ -193,8 +272,8 @@ static int mwifiex_dnld_cmd_to_fw(struct
cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&
cmd_code != HostCmd_CMD_FUNC_INIT) {
mwifiex_dbg(adapter, ERROR,
mwifiex_recycle_cmd_node(adapter, cmd_node);
queue_work(adapter->workqueue, &adapter->main_work);
return -1;
-@@ -660,8 +739,8 @@ int mwifiex_send_cmd(struct mwifiex_priv
+@@ -653,8 +732,8 @@ int mwifiex_send_cmd(struct mwifiex_priv
/* Return error, since the command preparation failed */
if (ret) {
mwifiex_dbg(adapter, ERROR,
mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
return -1;
}
-@@ -900,8 +979,9 @@ int mwifiex_process_cmdresp(struct mwifi
+@@ -902,8 +981,9 @@ int mwifiex_process_cmdresp(struct mwifi
if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
if (ret) {
mwifiex_dbg(adapter, ERROR,
mwifiex_init_fw_complete(adapter);
return -1;
} else if (adapter->last_init_cmd == cmdresp_no)
-@@ -1264,8 +1344,8 @@ mwifiex_process_sleep_confirm_resp(struc
+@@ -1273,8 +1353,8 @@ mwifiex_process_sleep_confirm_resp(struc
if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
mwifiex_dbg(adapter, ERROR,
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
-@@ -1108,6 +1108,8 @@ void mwifiex_cancel_all_pending_cmd(stru
+@@ -1099,6 +1099,8 @@ void mwifiex_cancel_all_pending_cmd(stru
void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter);
void mwifiex_cancel_scan(struct mwifiex_adapter *adapter);
--- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
-@@ -48,8 +48,9 @@ mwifiex_process_cmdresp_error(struct mwi
+@@ -36,8 +36,9 @@ mwifiex_process_cmdresp_error(struct mwi
struct host_cmd_ds_802_11_ps_mode_enh *pm;
mwifiex_dbg(adapter, ERROR,
adapter->cmd_wait_q.status = -1;
--- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
-@@ -806,7 +806,8 @@ int mwifiex_uap_prepare_cmd(struct mwifi
+@@ -794,7 +794,8 @@ int mwifiex_uap_prepare_cmd(struct mwifi
break;
default:
mwifiex_dbg(priv->adapter, ERROR,
+++ /dev/null
-From patchwork Sat Sep 17 20:26:27 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979242
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:26:27 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 01/16] rt2x00: define RF5592 in init_eeprom routine
-Message-ID:
- <d7eccb2c7b8ec4cd360fa2007796abffc35abb0d.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Fix incorrect RF value encoded in EEPROM on devices with Ralink Rt5592
-PCIe radio (a single chip 2T2R 802.11abgn solution).
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -9461,6 +9461,8 @@ static int rt2800_init_eeprom(struct rt2
- rf = RF3853;
- else if (rt2x00_rt(rt2x00dev, RT5350))
- rf = RF5350;
-+ else if (rt2x00_rt(rt2x00dev, RT5592))
-+ rf = RF5592;
- else
- rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
-
+++ /dev/null
-From patchwork Sat Sep 17 20:26:40 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979243
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:26:40 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 02/16] rt2x00: add throughput LED trigger
-Message-ID:
- <73f5ba4134e621462a26186449400cf0c1ac1730.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: David Bauer <mail@david-bauer.net>
-
-This adds a (currently missing) throughput LED trigger for the rt2x00
-driver. Previously, LED triggers had to be assigned to the netdev, which
-was limited to a single VAP.
-
-Tested-by: Christoph Krapp <achterin@googlemail.com>
-Signed-off-by: David Bauer <mail@david-bauer.net>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 18 ++++++++++++++++++
- 1 file changed, 18 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-@@ -1093,6 +1093,19 @@ static void rt2x00lib_remove_hw(struct r
- kfree(rt2x00dev->spec.channels_info);
- }
-
-+static const struct ieee80211_tpt_blink rt2x00_tpt_blink[] = {
-+ { .throughput = 0 * 1024, .blink_time = 334 },
-+ { .throughput = 1 * 1024, .blink_time = 260 },
-+ { .throughput = 2 * 1024, .blink_time = 220 },
-+ { .throughput = 5 * 1024, .blink_time = 190 },
-+ { .throughput = 10 * 1024, .blink_time = 170 },
-+ { .throughput = 25 * 1024, .blink_time = 150 },
-+ { .throughput = 54 * 1024, .blink_time = 130 },
-+ { .throughput = 120 * 1024, .blink_time = 110 },
-+ { .throughput = 265 * 1024, .blink_time = 80 },
-+ { .throughput = 586 * 1024, .blink_time = 50 },
-+};
-+
- static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
- {
- struct hw_mode_spec *spec = &rt2x00dev->spec;
-@@ -1174,6 +1187,11 @@ static int rt2x00lib_probe_hw(struct rt2
-
- #undef RT2X00_TASKLET_INIT
-
-+ ieee80211_create_tpt_led_trigger(rt2x00dev->hw,
-+ IEEE80211_TPT_LEDTRIG_FL_RADIO,
-+ rt2x00_tpt_blink,
-+ ARRAY_SIZE(rt2x00_tpt_blink));
-+
- /*
- * Register HW.
- */
+++ /dev/null
-From patchwork Sat Sep 17 20:26:55 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979244
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:26:55 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 03/16] rt2x00: add support for external PA on MT7620
-Message-ID:
- <af2c68ff831816a86fc39b0c10911c129a1f03dc.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-Implement support for external PA connected to MT7620A.
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-[pozega.tomislav@gmail.com: use chanreg and dccal helpers.]
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- drivers/net/wireless/ralink/rt2x00/rt2800.h | 1 +
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 52 ++++++++++++++++++-
- 2 files changed, 52 insertions(+), 1 deletion(-)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
-@@ -2739,6 +2739,7 @@ enum rt2800_eeprom_word {
- #define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f)
- #define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0)
- #define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600)
-+#define EEPROM_NIC_CONF2_EXTERNAL_PA FIELD16(0x8000)
-
- /*
- * EEPROM LNA
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -4372,6 +4372,43 @@ static void rt2800_config_channel(struct
- rt2800_iq_calibrate(rt2x00dev, rf->channel);
- }
-
-+ if (rt2x00_rt(rt2x00dev, RT6352)) {
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0,
-+ &rt2x00dev->cap_flags)) {
-+ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3);
-+ reg |= 0x00000101;
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, reg);
-+
-+ reg = rt2800_register_read(rt2x00dev, RF_BYPASS3);
-+ reg |= 0x00000101;
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg);
-+
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0x73);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0x73);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0x73);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0xC8);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xA4);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x05);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0xC8);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xA4);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x05);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x27);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0xC8);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xA4);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x05);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 05, 0x00);
-+
-+ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT,
-+ 0x36303636);
-+ rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN,
-+ 0x6C6C6B6C);
-+ rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN,
-+ 0x6C6C6B6C);
-+ }
-+ }
-+
- bbp = rt2800_bbp_read(rt2x00dev, 4);
- rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf));
- rt2800_bbp_write(rt2x00dev, 4, bbp);
-@@ -9592,7 +9629,8 @@ static int rt2800_init_eeprom(struct rt2
- */
- eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1);
-
-- if (rt2x00_rt(rt2x00dev, RT3352)) {
-+ if (rt2x00_rt(rt2x00dev, RT3352) ||
-+ rt2x00_rt(rt2x00dev, RT6352)) {
- if (rt2x00_get_field16(eeprom,
- EEPROM_NIC_CONF1_EXTERNAL_TX0_PA_3352))
- __set_bit(CAPABILITY_EXTERNAL_PA_TX0,
-@@ -9603,6 +9641,18 @@ static int rt2800_init_eeprom(struct rt2
- &rt2x00dev->cap_flags);
- }
-
-+ eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF2);
-+
-+ if (rt2x00_rt(rt2x00dev, RT6352) && eeprom != 0 && eeprom != 0xffff) {
-+ if (!rt2x00_get_field16(eeprom,
-+ EEPROM_NIC_CONF2_EXTERNAL_PA)) {
-+ __clear_bit(CAPABILITY_EXTERNAL_PA_TX0,
-+ &rt2x00dev->cap_flags);
-+ __clear_bit(CAPABILITY_EXTERNAL_PA_TX1,
-+ &rt2x00dev->cap_flags);
-+ }
-+ }
-+
- return 0;
- }
-
+++ /dev/null
-From patchwork Sat Sep 17 20:27:10 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979245
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:27:10 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 04/16] rt2x00: move up and reuse busy wait functions
-Message-ID:
- <3fdb9dc15e76a9f9c1948b4a3a1308a7a5677bb8.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-Move bbp_ready and rf_ready busy wait functions up in the code so they
-can more easily be used. Allow specifying register mask in rf_ready
-function which is useful for calibration routines which will be added
-in follow-up commits.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 99 +++++++++----------
- 1 file changed, 46 insertions(+), 53 deletions(-)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -2143,6 +2143,48 @@ void rt2800_config_erp(struct rt2x00_dev
- }
- EXPORT_SYMBOL_GPL(rt2800_config_erp);
-
-+static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev,
-+ const struct rt2x00_field32 mask)
-+{
-+ unsigned int i;
-+ u32 reg;
-+
-+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-+ reg = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG);
-+ if (!rt2x00_get_field32(reg, mask))
-+ return 0;
-+
-+ udelay(REGISTER_BUSY_DELAY);
-+ }
-+
-+ rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n");
-+ return -EACCES;
-+}
-+
-+static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
-+{
-+ unsigned int i;
-+ u8 value;
-+
-+ /*
-+ * BBP was enabled after firmware was loaded,
-+ * but we need to reactivate it now.
-+ */
-+ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
-+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
-+ msleep(1);
-+
-+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-+ value = rt2800_bbp_read(rt2x00dev, 0);
-+ if ((value != 0xff) && (value != 0x00))
-+ return 0;
-+ udelay(REGISTER_BUSY_DELAY);
-+ }
-+
-+ rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n");
-+ return -EACCES;
-+}
-+
- static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev)
- {
- u32 reg;
-@@ -3799,10 +3841,9 @@ static void rt2800_config_alc(struct rt2
- struct ieee80211_channel *chan,
- int power_level) {
- u16 eeprom, target_power, max_power;
-- u32 mac_sys_ctrl, mac_status;
-+ u32 mac_sys_ctrl;
- u32 reg;
- u8 bbp;
-- int i;
-
- /* hardware unit is 0.5dBm, limited to 23.5dBm */
- power_level *= 2;
-@@ -3838,16 +3879,8 @@ static void rt2800_config_alc(struct rt2
- /* Disable Tx/Rx */
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
- /* Check MAC Tx/Rx idle */
-- for (i = 0; i < 10000; i++) {
-- mac_status = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG);
-- if (mac_status & 0x3)
-- usleep_range(50, 200);
-- else
-- break;
-- }
--
-- if (i == 10000)
-- rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n");
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
-+ rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n");
-
- if (chan->center_freq > 2457) {
- bbp = rt2800_bbp_read(rt2x00dev, 30);
-@@ -6275,46 +6308,6 @@ static int rt2800_init_registers(struct
- return 0;
- }
-
--static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev)
--{
-- unsigned int i;
-- u32 reg;
--
-- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-- reg = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG);
-- if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY))
-- return 0;
--
-- udelay(REGISTER_BUSY_DELAY);
-- }
--
-- rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n");
-- return -EACCES;
--}
--
--static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
--{
-- unsigned int i;
-- u8 value;
--
-- /*
-- * BBP was enabled after firmware was loaded,
-- * but we need to reactivate it now.
-- */
-- rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
-- rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
-- msleep(1);
--
-- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-- value = rt2800_bbp_read(rt2x00dev, 0);
-- if ((value != 0xff) && (value != 0x00))
-- return 0;
-- udelay(REGISTER_BUSY_DELAY);
-- }
--
-- rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n");
-- return -EACCES;
--}
-
- static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev)
- {
-@@ -9136,7 +9129,7 @@ int rt2800_enable_radio(struct rt2x00_de
- /*
- * Wait BBP/RF to wake up.
- */
-- if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev)))
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
- return -EIO;
-
- /*
+++ /dev/null
-From patchwork Sat Sep 17 20:27:26 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979246
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:27:26 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 05/16] rt2x00: add RF self TXDC calibration for MT7620
-Message-ID:
- <dbb6e5a0c12d6101477bd09e83253091d21512c9.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Add TX self calibration based on mtk driver.
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
-v2: use ++i instead of i = i + 1 in loops
-
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 48 +++++++++++++++++++
- 1 file changed, 48 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -8454,6 +8454,53 @@ static void rt2800_init_rfcsr_5592(struc
- rt2800_led_open_drain_enable(rt2x00dev);
- }
-
-+static void rt2800_rf_self_txdc_cal(struct rt2x00_dev *rt2x00dev)
-+{
-+ u8 rfb5r1_org, rfb7r1_org, rfvalue;
-+ u32 mac0518, mac051c, mac0528, mac052c;
-+ u8 i;
-+
-+ mac0518 = rt2800_register_read(rt2x00dev, RF_CONTROL0);
-+ mac051c = rt2800_register_read(rt2x00dev, RF_BYPASS0);
-+ mac0528 = rt2800_register_read(rt2x00dev, RF_CONTROL2);
-+ mac052c = rt2800_register_read(rt2x00dev, RF_BYPASS2);
-+
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x0);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0x0);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0xC);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x3306);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL2, 0x3330);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0xfffff);
-+ rfb5r1_org = rt2800_rfcsr_read_bank(rt2x00dev, 5, 1);
-+ rfb7r1_org = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, 0x4);
-+ for (i = 0; i < 100; ++i) {
-+ usleep_range(50, 100);
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 1);
-+ if ((rfvalue & 0x04) != 0x4)
-+ break;
-+ }
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, rfb5r1_org);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, 0x4);
-+ for (i = 0; i < 100; ++i) {
-+ usleep_range(50, 100);
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1);
-+ if ((rfvalue & 0x04) != 0x4)
-+ break;
-+ }
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, rfb7r1_org);
-+
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x0);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS2, 0x0);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, mac0518);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, mac051c);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL2, mac0528);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS2, mac052c);
-+}
-+
- static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
- bool set_bw, bool is_ht40)
- {
-@@ -9061,6 +9108,7 @@ static void rt2800_init_rfcsr_6352(struc
- rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
- rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
-
-+ rt2800_rf_self_txdc_cal(rt2x00dev);
- rt2800_bw_filter_calibration(rt2x00dev, true);
- rt2800_bw_filter_calibration(rt2x00dev, false);
- }
+++ /dev/null
-From patchwork Sat Sep 17 20:27:41 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979247
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:27:41 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 06/16] rt2x00: add r calibration for MT7620
-Message-ID:
- <e0c34f233089bec4eb73826bc4f512166ee25934.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Add r calibration code as found in mtk driver.
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
-v2: use rt2800_wait_bbp_rf_ready()
-
- drivers/net/wireless/ralink/rt2x00/rt2800.h | 2 +
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 133 ++++++++++++++++++
- 2 files changed, 135 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
-@@ -1016,6 +1016,8 @@
- */
- #define MAC_STATUS_CFG 0x1200
- #define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003)
-+#define MAC_STATUS_CFG_BBP_RF_BUSY_TX FIELD32(0x00000001)
-+#define MAC_STATUS_CFG_BBP_RF_BUSY_RX FIELD32(0x00000002)
-
- /*
- * PWR_PIN_CFG:
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -8501,6 +8501,138 @@ static void rt2800_rf_self_txdc_cal(stru
- rt2800_register_write(rt2x00dev, RF_BYPASS2, mac052c);
- }
-
-+static int rt2800_calcrcalibrationcode(struct rt2x00_dev *rt2x00dev, int d1, int d2)
-+{
-+ int calcode = ((d2 - d1) * 1000) / 43;
-+
-+ if ((calcode % 10) >= 5)
-+ calcode += 10;
-+ calcode = (calcode / 10);
-+
-+ return calcode;
-+}
-+
-+static void rt2800_r_calibration(struct rt2x00_dev *rt2x00dev)
-+{
-+ u32 savemacsysctrl;
-+ u8 saverfb0r1, saverfb0r34, saverfb0r35;
-+ u8 saverfb5r4, saverfb5r17, saverfb5r18;
-+ u8 saverfb5r19, saverfb5r20;
-+ u8 savebbpr22, savebbpr47, savebbpr49;
-+ u8 bytevalue = 0;
-+ int rcalcode;
-+ u8 r_cal_code = 0;
-+ char d1 = 0, d2 = 0;
-+ u8 rfvalue;
-+ u32 MAC_RF_BYPASS0, MAC_RF_CONTROL0, MAC_PWR_PIN_CFG;
-+ u32 maccfg;
-+
-+ saverfb0r1 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1);
-+ saverfb0r34 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 34);
-+ saverfb0r35 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35);
-+ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);
-+ saverfb5r17 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17);
-+ saverfb5r18 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18);
-+ saverfb5r19 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19);
-+ saverfb5r20 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20);
-+
-+ savebbpr22 = rt2800_bbp_read(rt2x00dev, 22);
-+ savebbpr47 = rt2800_bbp_read(rt2x00dev, 47);
-+ savebbpr49 = rt2800_bbp_read(rt2x00dev, 49);
-+
-+ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ MAC_RF_BYPASS0 = rt2800_register_read(rt2x00dev, RF_BYPASS0);
-+ MAC_RF_CONTROL0 = rt2800_register_read(rt2x00dev, RF_CONTROL0);
-+ MAC_PWR_PIN_CFG = rt2800_register_read(rt2x00dev, PWR_PIN_CFG);
-+
-+ maccfg = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ maccfg &= (~0x04);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX)))
-+ rt2x00_warn(rt2x00dev, "Wait MAC Tx Status to MAX !!!\n");
-+
-+ maccfg = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ maccfg &= (~0x04);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX)))
-+ rt2x00_warn(rt2x00dev, "Wait MAC Rx Status to MAX !!!\n");
-+
-+ rfvalue = (MAC_RF_BYPASS0 | 0x3004);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, rfvalue);
-+ rfvalue = (MAC_RF_CONTROL0 | (~0x3002));
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, rfvalue);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, 0x27);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, 0x80);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0x83);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0x00);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x20);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x00);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 34, 0x13);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00);
-+
-+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x1);
-+
-+ rt2800_bbp_write(rt2x00dev, 47, 0x04);
-+ rt2800_bbp_write(rt2x00dev, 22, 0x80);
-+ usleep_range(100, 200);
-+ bytevalue = rt2800_bbp_read(rt2x00dev, 49);
-+ if (bytevalue > 128)
-+ d1 = bytevalue - 256;
-+ else
-+ d1 = (char)bytevalue;
-+ rt2800_bbp_write(rt2x00dev, 22, 0x0);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x01);
-+
-+ rt2800_bbp_write(rt2x00dev, 22, 0x80);
-+ usleep_range(100, 200);
-+ bytevalue = rt2800_bbp_read(rt2x00dev, 49);
-+ if (bytevalue > 128)
-+ d2 = bytevalue - 256;
-+ else
-+ d2 = (char)bytevalue;
-+ rt2800_bbp_write(rt2x00dev, 22, 0x0);
-+
-+ rcalcode = rt2800_calcrcalibrationcode(rt2x00dev, d1, d2);
-+ if (rcalcode < 0)
-+ r_cal_code = 256 + rcalcode;
-+ else
-+ r_cal_code = (u8)rcalcode;
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 7, r_cal_code);
-+
-+ rt2800_bbp_write(rt2x00dev, 22, 0x0);
-+
-+ bytevalue = rt2800_bbp_read(rt2x00dev, 21);
-+ bytevalue |= 0x1;
-+ rt2800_bbp_write(rt2x00dev, 21, bytevalue);
-+ bytevalue = rt2800_bbp_read(rt2x00dev, 21);
-+ bytevalue &= (~0x1);
-+ rt2800_bbp_write(rt2x00dev, 21, bytevalue);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, saverfb0r1);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 34, saverfb0r34);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, saverfb0r35);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, saverfb5r4);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, saverfb5r17);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, saverfb5r18);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, saverfb5r19);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, saverfb5r20);
-+
-+ rt2800_bbp_write(rt2x00dev, 22, savebbpr22);
-+ rt2800_bbp_write(rt2x00dev, 47, savebbpr47);
-+ rt2800_bbp_write(rt2x00dev, 49, savebbpr49);
-+
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, MAC_RF_BYPASS0);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, MAC_RF_CONTROL0);
-+
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl);
-+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, MAC_PWR_PIN_CFG);
-+}
-+
- static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
- bool set_bw, bool is_ht40)
- {
-@@ -9108,6 +9240,7 @@ static void rt2800_init_rfcsr_6352(struc
- rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00);
- rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C);
-
-+ rt2800_r_calibration(rt2x00dev);
- rt2800_rf_self_txdc_cal(rt2x00dev);
- rt2800_bw_filter_calibration(rt2x00dev, true);
- rt2800_bw_filter_calibration(rt2x00dev, false);
+++ /dev/null
-From patchwork Sat Sep 17 20:27:56 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979248
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:27:56 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 07/16] rt2x00: add RXDCOC calibration for MT7620
-Message-ID:
- <850b30f652e88de30d79e968af4eb47aa5bc2511.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Add RXDCOC calibration code from mtk driver.
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-[fixed typo reported by Serge Vasilugin <vasilugin@yandex.ru>]
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 60 +++++++++++++++++++
- 1 file changed, 60 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -8633,6 +8633,65 @@ static void rt2800_r_calibration(struct
- rt2800_register_write(rt2x00dev, PWR_PIN_CFG, MAC_PWR_PIN_CFG);
- }
-
-+static void rt2800_rxdcoc_calibration(struct rt2x00_dev *rt2x00dev)
-+{
-+ u8 bbpreg = 0;
-+ u32 macvalue = 0;
-+ u8 saverfb0r2, saverfb5r4, saverfb7r4, rfvalue;
-+ int i;
-+
-+ saverfb0r2 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2);
-+ rfvalue = saverfb0r2;
-+ rfvalue |= 0x03;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfvalue);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 141);
-+ bbpreg = rt2800_bbp_read(rt2x00dev, 159);
-+ bbpreg |= 0x10;
-+ rt2800_bbp_write(rt2x00dev, 159, bbpreg);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x8);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX)))
-+ rt2x00_warn(rt2x00dev, "RF TX busy in RX RXDCOC calibration\n");
-+
-+ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);
-+ saverfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4);
-+ saverfb5r4 = saverfb5r4 & (~0x40);
-+ saverfb7r4 = saverfb7r4 & (~0x40);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x64);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, saverfb5r4);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, saverfb7r4);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 141);
-+ bbpreg = rt2800_bbp_read(rt2x00dev, 159);
-+ bbpreg = bbpreg & (~0x40);
-+ rt2800_bbp_write(rt2x00dev, 159, bbpreg);
-+ bbpreg |= 0x48;
-+ rt2800_bbp_write(rt2x00dev, 159, bbpreg);
-+
-+ for (i = 0; i < 10000; i++) {
-+ bbpreg = rt2800_bbp_read(rt2x00dev, 159);
-+ if ((bbpreg & 0x40) == 0)
-+ break;
-+ usleep_range(50, 100);
-+ }
-+
-+ bbpreg = rt2800_bbp_read(rt2x00dev, 159);
-+ bbpreg = bbpreg & (~0x40);
-+ rt2800_bbp_write(rt2x00dev, 159, bbpreg);
-+
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 141);
-+ bbpreg = rt2800_bbp_read(rt2x00dev, 159);
-+ bbpreg &= (~0x10);
-+ rt2800_bbp_write(rt2x00dev, 159, bbpreg);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, saverfb0r2);
-+}
-+
- static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
- bool set_bw, bool is_ht40)
- {
-@@ -9242,6 +9301,7 @@ static void rt2800_init_rfcsr_6352(struc
-
- rt2800_r_calibration(rt2x00dev);
- rt2800_rf_self_txdc_cal(rt2x00dev);
-+ rt2800_rxdcoc_calibration(rt2x00dev);
- rt2800_bw_filter_calibration(rt2x00dev, true);
- rt2800_bw_filter_calibration(rt2x00dev, false);
- }
+++ /dev/null
-From patchwork Sat Sep 17 20:28:10 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979249
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:28:10 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 08/16] rt2x00: add RXIQ calibration for MT7620
-Message-ID:
- <033a39a697d51f6df258acea4c33608e0944fe4c.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Add RXIQ calibration found in mtk driver. With old openwrt builds this
-gets us ~8Mbps more of RX bandwidth (test with iPA/eLNA layout).
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
-v2: use rt2800_wait_bbp_rf_ready(), fix indentation
-
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 375 ++++++++++++++++++
- 1 file changed, 375 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -8692,6 +8692,380 @@ static void rt2800_rxdcoc_calibration(st
- rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, saverfb0r2);
- }
-
-+static u32 rt2800_do_sqrt_accumulation(u32 si)
-+{
-+ u32 root, root_pre, bit;
-+ char i;
-+
-+ bit = 1 << 15;
-+ root = 0;
-+ for (i = 15; i >= 0; i = i - 1) {
-+ root_pre = root + bit;
-+ if ((root_pre * root_pre) <= si)
-+ root = root_pre;
-+ bit = bit >> 1;
-+ }
-+
-+ return root;
-+}
-+
-+static void rt2800_rxiq_calibration(struct rt2x00_dev *rt2x00dev)
-+{
-+ u8 rfb0r1, rfb0r2, rfb0r42;
-+ u8 rfb4r0, rfb4r19;
-+ u8 rfb5r3, rfb5r4, rfb5r17, rfb5r18, rfb5r19, rfb5r20;
-+ u8 rfb6r0, rfb6r19;
-+ u8 rfb7r3, rfb7r4, rfb7r17, rfb7r18, rfb7r19, rfb7r20;
-+
-+ u8 bbp1, bbp4;
-+ u8 bbpr241, bbpr242;
-+ u32 i;
-+ u8 ch_idx;
-+ u8 bbpval;
-+ u8 rfval, vga_idx = 0;
-+ int mi = 0, mq = 0, si = 0, sq = 0, riq = 0;
-+ int sigma_i, sigma_q, r_iq, g_rx;
-+ int g_imb;
-+ int ph_rx;
-+ u32 savemacsysctrl = 0;
-+ u32 orig_RF_CONTROL0 = 0;
-+ u32 orig_RF_BYPASS0 = 0;
-+ u32 orig_RF_CONTROL1 = 0;
-+ u32 orig_RF_BYPASS1 = 0;
-+ u32 orig_RF_CONTROL3 = 0;
-+ u32 orig_RF_BYPASS3 = 0;
-+ u32 bbpval1 = 0;
-+ static const u8 rf_vga_table[] = {0x20, 0x21, 0x22, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f};
-+
-+ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ orig_RF_CONTROL0 = rt2800_register_read(rt2x00dev, RF_CONTROL0);
-+ orig_RF_BYPASS0 = rt2800_register_read(rt2x00dev, RF_BYPASS0);
-+ orig_RF_CONTROL1 = rt2800_register_read(rt2x00dev, RF_CONTROL1);
-+ orig_RF_BYPASS1 = rt2800_register_read(rt2x00dev, RF_BYPASS1);
-+ orig_RF_CONTROL3 = rt2800_register_read(rt2x00dev, RF_CONTROL3);
-+ orig_RF_BYPASS3 = rt2800_register_read(rt2x00dev, RF_BYPASS3);
-+
-+ bbp1 = rt2800_bbp_read(rt2x00dev, 1);
-+ bbp4 = rt2800_bbp_read(rt2x00dev, 4);
-+
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x0);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY)))
-+ rt2x00_warn(rt2x00dev, "Timeout waiting for MAC status in RXIQ calibration\n");
-+
-+ bbpval = bbp4 & (~0x18);
-+ bbpval = bbp4 | 0x00;
-+ rt2800_bbp_write(rt2x00dev, 4, bbpval);
-+
-+ bbpval = rt2800_bbp_read(rt2x00dev, 21);
-+ bbpval = bbpval | 1;
-+ rt2800_bbp_write(rt2x00dev, 21, bbpval);
-+ bbpval = bbpval & 0xfe;
-+ rt2800_bbp_write(rt2x00dev, 21, bbpval);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL1, 0x00000202);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS1, 0x00000303);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags))
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x0101);
-+ else
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x0000);
-+
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0xf1f1);
-+
-+ rfb0r1 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1);
-+ rfb0r2 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2);
-+ rfb0r42 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42);
-+ rfb4r0 = rt2800_rfcsr_read_bank(rt2x00dev, 4, 0);
-+ rfb4r19 = rt2800_rfcsr_read_bank(rt2x00dev, 4, 19);
-+ rfb5r3 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 3);
-+ rfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);
-+ rfb5r17 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17);
-+ rfb5r18 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18);
-+ rfb5r19 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19);
-+ rfb5r20 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20);
-+
-+ rfb6r0 = rt2800_rfcsr_read_bank(rt2x00dev, 6, 0);
-+ rfb6r19 = rt2800_rfcsr_read_bank(rt2x00dev, 6, 19);
-+ rfb7r3 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 3);
-+ rfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4);
-+ rfb7r17 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 17);
-+ rfb7r18 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 18);
-+ rfb7r19 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 19);
-+ rfb7r20 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 20);
-+
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x87);
-+ rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0x27);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x38);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x38);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x80);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 18, 0xC1);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 19, 0x60);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 20, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 23, 0x0);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x0);
-+
-+ rt2800_bbp_dcoc_write(rt2x00dev, 5, 0x0);
-+
-+ bbpr241 = rt2800_bbp_read(rt2x00dev, 241);
-+ bbpr242 = rt2800_bbp_read(rt2x00dev, 242);
-+
-+ rt2800_bbp_write(rt2x00dev, 241, 0x10);
-+ rt2800_bbp_write(rt2x00dev, 242, 0x84);
-+ rt2800_bbp_write(rt2x00dev, 244, 0x31);
-+
-+ bbpval = rt2800_bbp_dcoc_read(rt2x00dev, 3);
-+ bbpval = bbpval & (~0x7);
-+ rt2800_bbp_dcoc_write(rt2x00dev, 3, bbpval);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000006);
-+ usleep_range(1, 200);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003376);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001006);
-+ udelay(1);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x06);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x06);
-+ } else {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x02);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x02);
-+ }
-+
-+ for (ch_idx = 0; ch_idx < 2; ch_idx = ch_idx + 1) {
-+ if (ch_idx == 0) {
-+ rfval = rfb0r1 & (~0x3);
-+ rfval = rfb0r1 | 0x1;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfval);
-+ rfval = rfb0r2 & (~0x33);
-+ rfval = rfb0r2 | 0x11;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfval);
-+ rfval = rfb0r42 & (~0x50);
-+ rfval = rfb0r42 | 0x10;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfval);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001006);
-+ udelay(1);
-+
-+ bbpval = bbp1 & (~0x18);
-+ bbpval = bbpval | 0x00;
-+ rt2800_bbp_write(rt2x00dev, 1, bbpval);
-+
-+ rt2800_bbp_dcoc_write(rt2x00dev, 1, 0x00);
-+ } else {
-+ rfval = rfb0r1 & (~0x3);
-+ rfval = rfb0r1 | 0x2;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfval);
-+ rfval = rfb0r2 & (~0x33);
-+ rfval = rfb0r2 | 0x22;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfval);
-+ rfval = rfb0r42 & (~0x50);
-+ rfval = rfb0r42 | 0x40;
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfval);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002006);
-+ udelay(1);
-+
-+ bbpval = bbp1 & (~0x18);
-+ bbpval = bbpval | 0x08;
-+ rt2800_bbp_write(rt2x00dev, 1, bbpval);
-+
-+ rt2800_bbp_dcoc_write(rt2x00dev, 1, 0x01);
-+ }
-+ usleep_range(500, 1500);
-+
-+ vga_idx = 0;
-+ while (vga_idx < 11) {
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rf_vga_table[vga_idx]);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rf_vga_table[vga_idx]);
-+
-+ rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x93);
-+
-+ for (i = 0; i < 10000; i++) {
-+ bbpval = rt2800_bbp_read(rt2x00dev, 159);
-+ if ((bbpval & 0xff) == 0x93)
-+ usleep_range(50, 100);
-+ else
-+ break;
-+ }
-+
-+ if ((bbpval & 0xff) == 0x93) {
-+ rt2x00_warn(rt2x00dev, "Fatal Error: Calibration doesn't finish");
-+ goto restore_value;
-+ }
-+ for (i = 0; i < 5; i++) {
-+ u32 bbptemp = 0;
-+ u8 value = 0;
-+ int result = 0;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x1e);
-+ rt2800_bbp_write(rt2x00dev, 159, i);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x22);
-+ value = rt2800_bbp_read(rt2x00dev, 159);
-+ bbptemp = bbptemp + (value << 24);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x21);
-+ value = rt2800_bbp_read(rt2x00dev, 159);
-+ bbptemp = bbptemp + (value << 16);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x20);
-+ value = rt2800_bbp_read(rt2x00dev, 159);
-+ bbptemp = bbptemp + (value << 8);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x1f);
-+ value = rt2800_bbp_read(rt2x00dev, 159);
-+ bbptemp = bbptemp + value;
-+
-+ if (i < 2 && (bbptemp & 0x800000))
-+ result = (bbptemp & 0xffffff) - 0x1000000;
-+ else if (i == 4)
-+ result = bbptemp;
-+ else
-+ result = bbptemp;
-+
-+ if (i == 0)
-+ mi = result / 4096;
-+ else if (i == 1)
-+ mq = result / 4096;
-+ else if (i == 2)
-+ si = bbptemp / 4096;
-+ else if (i == 3)
-+ sq = bbptemp / 4096;
-+ else
-+ riq = result / 4096;
-+ }
-+
-+ bbpval1 = si - mi * mi;
-+ rt2x00_dbg(rt2x00dev,
-+ "RXIQ si=%d, sq=%d, riq=%d, bbpval %d, vga_idx %d",
-+ si, sq, riq, bbpval1, vga_idx);
-+
-+ if (bbpval1 >= (100 * 100))
-+ break;
-+
-+ if (bbpval1 <= 100)
-+ vga_idx = vga_idx + 9;
-+ else if (bbpval1 <= 158)
-+ vga_idx = vga_idx + 8;
-+ else if (bbpval1 <= 251)
-+ vga_idx = vga_idx + 7;
-+ else if (bbpval1 <= 398)
-+ vga_idx = vga_idx + 6;
-+ else if (bbpval1 <= 630)
-+ vga_idx = vga_idx + 5;
-+ else if (bbpval1 <= 1000)
-+ vga_idx = vga_idx + 4;
-+ else if (bbpval1 <= 1584)
-+ vga_idx = vga_idx + 3;
-+ else if (bbpval1 <= 2511)
-+ vga_idx = vga_idx + 2;
-+ else
-+ vga_idx = vga_idx + 1;
-+ }
-+
-+ sigma_i = rt2800_do_sqrt_accumulation(100 * (si - mi * mi));
-+ sigma_q = rt2800_do_sqrt_accumulation(100 * (sq - mq * mq));
-+ r_iq = 10 * (riq - (mi * mq));
-+
-+ rt2x00_dbg(rt2x00dev, "Sigma_i=%d, Sigma_q=%d, R_iq=%d", sigma_i, sigma_q, r_iq);
-+
-+ if (sigma_i <= 1400 && sigma_i >= 1000 &&
-+ (sigma_i - sigma_q) <= 112 &&
-+ (sigma_i - sigma_q) >= -112 &&
-+ mi <= 32 && mi >= -32 &&
-+ mq <= 32 && mq >= -32) {
-+ r_iq = 10 * (riq - (mi * mq));
-+ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n",
-+ sigma_i, sigma_q, r_iq);
-+
-+ g_rx = (1000 * sigma_q) / sigma_i;
-+ g_imb = ((-2) * 128 * (1000 - g_rx)) / (1000 + g_rx);
-+ ph_rx = (r_iq * 2292) / (sigma_i * sigma_q);
-+
-+ if (ph_rx > 20 || ph_rx < -20) {
-+ ph_rx = 0;
-+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL");
-+ }
-+
-+ if (g_imb > 12 || g_imb < -12) {
-+ g_imb = 0;
-+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL");
-+ }
-+ } else {
-+ g_imb = 0;
-+ ph_rx = 0;
-+ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n",
-+ sigma_i, sigma_q, r_iq);
-+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL");
-+ }
-+
-+ if (ch_idx == 0) {
-+ rt2800_bbp_write(rt2x00dev, 158, 0x37);
-+ rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x35);
-+ rt2800_bbp_write(rt2x00dev, 159, ph_rx & 0x3f);
-+ } else {
-+ rt2800_bbp_write(rt2x00dev, 158, 0x55);
-+ rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f);
-+ rt2800_bbp_write(rt2x00dev, 158, 0x53);
-+ rt2800_bbp_write(rt2x00dev, 159, ph_rx & 0x3f);
-+ }
-+ }
-+
-+restore_value:
-+ rt2800_bbp_write(rt2x00dev, 158, 0x3);
-+ bbpval = rt2800_bbp_read(rt2x00dev, 159);
-+ rt2800_bbp_write(rt2x00dev, 159, (bbpval | 0x07));
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 1, bbp1);
-+ rt2800_bbp_write(rt2x00dev, 4, bbp4);
-+ rt2800_bbp_write(rt2x00dev, 241, bbpr241);
-+ rt2800_bbp_write(rt2x00dev, 242, bbpr242);
-+
-+ rt2800_bbp_write(rt2x00dev, 244, 0x00);
-+ bbpval = rt2800_bbp_read(rt2x00dev, 21);
-+ bbpval |= 0x1;
-+ rt2800_bbp_write(rt2x00dev, 21, bbpval);
-+ usleep_range(10, 200);
-+ bbpval &= 0xfe;
-+ rt2800_bbp_write(rt2x00dev, 21, bbpval);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, rfb0r1);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, rfb0r2);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfb0r42);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 4, 0, rfb4r0);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 4, 19, rfb4r19);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, rfb5r3);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, rfb5r4);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, rfb5r17);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, rfb5r18);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, rfb5r19);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, rfb5r20);
-+
-+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 0, rfb6r0);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 19, rfb6r19);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 3, rfb7r3);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, rfb7r4);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 17, rfb7r17);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 18, rfb7r18);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 19, rfb7r19);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 20, rfb7r20);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000006);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, orig_RF_CONTROL0);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, orig_RF_BYPASS0);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL1, orig_RF_CONTROL1);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS1, orig_RF_BYPASS1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, orig_RF_CONTROL3);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, orig_RF_BYPASS3);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl);
-+}
-+
- static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
- bool set_bw, bool is_ht40)
- {
-@@ -9304,6 +9678,7 @@ static void rt2800_init_rfcsr_6352(struc
- rt2800_rxdcoc_calibration(rt2x00dev);
- rt2800_bw_filter_calibration(rt2x00dev, true);
- rt2800_bw_filter_calibration(rt2x00dev, false);
-+ rt2800_rxiq_calibration(rt2x00dev);
- }
-
- static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
+++ /dev/null
-From patchwork Sat Sep 17 20:28:43 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 8bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979251
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:28:43 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 10/16] rt2x00: add TX LOFT calibration for MT7620
-Message-ID:
- <d9295a9138a1f552b648aacb84e1419d38f5c896.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-From: Tomislav Požega <pozega.tomislav@gmail.com>
-
-Add TX LOFT calibration from mtk driver.
-
-Signed-off-by: Tomislav Požega <pozega.tomislav@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
-v2: use helper functions, make tables static const, remove useless
- debug prints
-v3: don't export function not used anywhere else
-Reported-by: kernel test robot <lkp@intel.com>
-
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 902 ++++++++++++++++++
- .../net/wireless/ralink/rt2x00/rt2800lib.h | 10 +
- 2 files changed, 912 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -9066,6 +9066,907 @@ restore_value:
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl);
- }
-
-+static void rt2800_rf_configstore(struct rt2x00_dev *rt2x00dev,
-+ struct rf_reg_pair rf_reg_record[][13], u8 chain)
-+{
-+ u8 rfvalue = 0;
-+
-+ if (chain == CHAIN_0) {
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1);
-+ rf_reg_record[CHAIN_0][0].bank = 0;
-+ rf_reg_record[CHAIN_0][0].reg = 1;
-+ rf_reg_record[CHAIN_0][0].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2);
-+ rf_reg_record[CHAIN_0][1].bank = 0;
-+ rf_reg_record[CHAIN_0][1].reg = 2;
-+ rf_reg_record[CHAIN_0][1].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35);
-+ rf_reg_record[CHAIN_0][2].bank = 0;
-+ rf_reg_record[CHAIN_0][2].reg = 35;
-+ rf_reg_record[CHAIN_0][2].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42);
-+ rf_reg_record[CHAIN_0][3].bank = 0;
-+ rf_reg_record[CHAIN_0][3].reg = 42;
-+ rf_reg_record[CHAIN_0][3].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 0);
-+ rf_reg_record[CHAIN_0][4].bank = 4;
-+ rf_reg_record[CHAIN_0][4].reg = 0;
-+ rf_reg_record[CHAIN_0][4].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 2);
-+ rf_reg_record[CHAIN_0][5].bank = 4;
-+ rf_reg_record[CHAIN_0][5].reg = 2;
-+ rf_reg_record[CHAIN_0][5].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 4, 34);
-+ rf_reg_record[CHAIN_0][6].bank = 4;
-+ rf_reg_record[CHAIN_0][6].reg = 34;
-+ rf_reg_record[CHAIN_0][6].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 3);
-+ rf_reg_record[CHAIN_0][7].bank = 5;
-+ rf_reg_record[CHAIN_0][7].reg = 3;
-+ rf_reg_record[CHAIN_0][7].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);
-+ rf_reg_record[CHAIN_0][8].bank = 5;
-+ rf_reg_record[CHAIN_0][8].reg = 4;
-+ rf_reg_record[CHAIN_0][8].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 17);
-+ rf_reg_record[CHAIN_0][9].bank = 5;
-+ rf_reg_record[CHAIN_0][9].reg = 17;
-+ rf_reg_record[CHAIN_0][9].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 18);
-+ rf_reg_record[CHAIN_0][10].bank = 5;
-+ rf_reg_record[CHAIN_0][10].reg = 18;
-+ rf_reg_record[CHAIN_0][10].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 19);
-+ rf_reg_record[CHAIN_0][11].bank = 5;
-+ rf_reg_record[CHAIN_0][11].reg = 19;
-+ rf_reg_record[CHAIN_0][11].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 20);
-+ rf_reg_record[CHAIN_0][12].bank = 5;
-+ rf_reg_record[CHAIN_0][12].reg = 20;
-+ rf_reg_record[CHAIN_0][12].value = rfvalue;
-+ } else if (chain == CHAIN_1) {
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1);
-+ rf_reg_record[CHAIN_1][0].bank = 0;
-+ rf_reg_record[CHAIN_1][0].reg = 1;
-+ rf_reg_record[CHAIN_1][0].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 2);
-+ rf_reg_record[CHAIN_1][1].bank = 0;
-+ rf_reg_record[CHAIN_1][1].reg = 2;
-+ rf_reg_record[CHAIN_1][1].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 35);
-+ rf_reg_record[CHAIN_1][2].bank = 0;
-+ rf_reg_record[CHAIN_1][2].reg = 35;
-+ rf_reg_record[CHAIN_1][2].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42);
-+ rf_reg_record[CHAIN_1][3].bank = 0;
-+ rf_reg_record[CHAIN_1][3].reg = 42;
-+ rf_reg_record[CHAIN_1][3].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 0);
-+ rf_reg_record[CHAIN_1][4].bank = 6;
-+ rf_reg_record[CHAIN_1][4].reg = 0;
-+ rf_reg_record[CHAIN_1][4].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 2);
-+ rf_reg_record[CHAIN_1][5].bank = 6;
-+ rf_reg_record[CHAIN_1][5].reg = 2;
-+ rf_reg_record[CHAIN_1][5].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 6, 34);
-+ rf_reg_record[CHAIN_1][6].bank = 6;
-+ rf_reg_record[CHAIN_1][6].reg = 34;
-+ rf_reg_record[CHAIN_1][6].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 3);
-+ rf_reg_record[CHAIN_1][7].bank = 7;
-+ rf_reg_record[CHAIN_1][7].reg = 3;
-+ rf_reg_record[CHAIN_1][7].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4);
-+ rf_reg_record[CHAIN_1][8].bank = 7;
-+ rf_reg_record[CHAIN_1][8].reg = 4;
-+ rf_reg_record[CHAIN_1][8].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 17);
-+ rf_reg_record[CHAIN_1][9].bank = 7;
-+ rf_reg_record[CHAIN_1][9].reg = 17;
-+ rf_reg_record[CHAIN_1][9].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 18);
-+ rf_reg_record[CHAIN_1][10].bank = 7;
-+ rf_reg_record[CHAIN_1][10].reg = 18;
-+ rf_reg_record[CHAIN_1][10].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 19);
-+ rf_reg_record[CHAIN_1][11].bank = 7;
-+ rf_reg_record[CHAIN_1][11].reg = 19;
-+ rf_reg_record[CHAIN_1][11].value = rfvalue;
-+ rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 20);
-+ rf_reg_record[CHAIN_1][12].bank = 7;
-+ rf_reg_record[CHAIN_1][12].reg = 20;
-+ rf_reg_record[CHAIN_1][12].value = rfvalue;
-+ } else {
-+ rt2x00_warn(rt2x00dev, "Unknown chain = %u\n", chain);
-+ }
-+}
-+
-+static void rt2800_rf_configrecover(struct rt2x00_dev *rt2x00dev,
-+ struct rf_reg_pair rf_record[][13])
-+{
-+ u8 chain_index = 0, record_index = 0;
-+ u8 bank = 0, rf_register = 0, value = 0;
-+
-+ for (chain_index = 0; chain_index < 2; chain_index++) {
-+ for (record_index = 0; record_index < 13; record_index++) {
-+ bank = rf_record[chain_index][record_index].bank;
-+ rf_register = rf_record[chain_index][record_index].reg;
-+ value = rf_record[chain_index][record_index].value;
-+ rt2800_rfcsr_write_bank(rt2x00dev, bank, rf_register, value);
-+ rt2x00_dbg(rt2x00dev, "bank: %d, rf_register: %d, value: %x\n",
-+ bank, rf_register, value);
-+ }
-+ }
-+}
-+
-+static void rt2800_setbbptonegenerator(struct rt2x00_dev *rt2x00dev)
-+{
-+ rt2800_bbp_write(rt2x00dev, 158, 0xAA);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xAB);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x0A);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xAC);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x3F);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xAD);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x3F);
-+
-+ rt2800_bbp_write(rt2x00dev, 244, 0x40);
-+}
-+
-+static u32 rt2800_do_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx, u8 read_neg)
-+{
-+ u32 macvalue = 0;
-+ int fftout_i = 0, fftout_q = 0;
-+ u32 ptmp = 0, pint = 0;
-+ u8 bbp = 0;
-+ u8 tidxi;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x9b);
-+
-+ bbp = 0x9b;
-+
-+ while (bbp == 0x9b) {
-+ usleep_range(10, 50);
-+ bbp = rt2800_bbp_read(rt2x00dev, 159);
-+ bbp = bbp & 0xff;
-+ }
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xba);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, 0x057C);
-+
-+ fftout_i = (macvalue >> 16);
-+ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i;
-+ fftout_q = (macvalue & 0xffff);
-+ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q;
-+ ptmp = (fftout_i * fftout_i);
-+ ptmp = ptmp + (fftout_q * fftout_q);
-+ pint = ptmp;
-+ rt2x00_dbg(rt2x00dev, "I = %d, Q = %d, power = %x\n", fftout_i, fftout_q, pint);
-+ if (read_neg) {
-+ pint = pint >> 1;
-+ tidxi = 0x40 - tidx;
-+ tidxi = tidxi & 0x3f;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xba);
-+ rt2800_bbp_write(rt2x00dev, 159, tidxi);
-+ rt2800_bbp_write(rt2x00dev, 159, tidxi);
-+ rt2800_bbp_write(rt2x00dev, 159, tidxi);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, 0x057C);
-+
-+ fftout_i = (macvalue >> 16);
-+ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i;
-+ fftout_q = (macvalue & 0xffff);
-+ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q;
-+ ptmp = (fftout_i * fftout_i);
-+ ptmp = ptmp + (fftout_q * fftout_q);
-+ ptmp = ptmp >> 1;
-+ pint = pint + ptmp;
-+ }
-+
-+ return pint;
-+}
-+
-+static u32 rt2800_read_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx)
-+{
-+ u32 macvalue = 0;
-+ int fftout_i = 0, fftout_q = 0;
-+ u32 ptmp = 0, pint = 0;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xBA);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+ rt2800_bbp_write(rt2x00dev, 159, tidx);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, 0x057C);
-+
-+ fftout_i = (macvalue >> 16);
-+ fftout_i = (fftout_i & 0x8000) ? (fftout_i - 0x10000) : fftout_i;
-+ fftout_q = (macvalue & 0xffff);
-+ fftout_q = (fftout_q & 0x8000) ? (fftout_q - 0x10000) : fftout_q;
-+ ptmp = (fftout_i * fftout_i);
-+ ptmp = ptmp + (fftout_q * fftout_q);
-+ pint = ptmp;
-+
-+ return pint;
-+}
-+
-+static void rt2800_write_dc(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 alc, u8 iorq, u8 dc)
-+{
-+ u8 bbp = 0;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb0);
-+ bbp = alc | 0x80;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+
-+ if (ch_idx == 0)
-+ bbp = (iorq == 0) ? 0xb1 : 0xb2;
-+ else
-+ bbp = (iorq == 0) ? 0xb8 : 0xb9;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ bbp = dc;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+}
-+
-+static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx,
-+ u8 alc_idx, u8 dc_result[][RF_ALC_NUM][2])
-+{
-+ u32 p0 = 0, p1 = 0, pf = 0;
-+ char idx0 = 0, idx1 = 0;
-+ u8 idxf[] = {0x00, 0x00};
-+ u8 ibit = 0x20;
-+ u8 iorq;
-+ char bidx;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb0);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x80);
-+
-+ for (bidx = 5; bidx >= 0; bidx--) {
-+ for (iorq = 0; iorq <= 1; iorq++) {
-+ if (idxf[iorq] == 0x20) {
-+ idx0 = 0x20;
-+ p0 = pf;
-+ } else {
-+ idx0 = idxf[iorq] - ibit;
-+ idx0 = idx0 & 0x3F;
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idx0);
-+ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0);
-+ }
-+
-+ idx1 = idxf[iorq] + (bidx == 5 ? 0 : ibit);
-+ idx1 = idx1 & 0x3F;
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idx1);
-+ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0);
-+
-+ rt2x00_dbg(rt2x00dev, "alc=%u, IorQ=%u, idx_final=%2x\n",
-+ alc_idx, iorq, idxf[iorq]);
-+ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pf=%x, idx_0=%x, idx_1=%x, ibit=%x\n",
-+ p0, p1, pf, idx0, idx1, ibit);
-+
-+ if (bidx != 5 && pf <= p0 && pf < p1) {
-+ idxf[iorq] = idxf[iorq];
-+ } else if (p0 < p1) {
-+ pf = p0;
-+ idxf[iorq] = idx0 & 0x3F;
-+ } else {
-+ pf = p1;
-+ idxf[iorq] = idx1 & 0x3F;
-+ }
-+ rt2x00_dbg(rt2x00dev, "IorQ=%u, idx_final[%u]:%x, pf:%8x\n",
-+ iorq, iorq, idxf[iorq], pf);
-+
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idxf[iorq]);
-+ }
-+ ibit = ibit >> 1;
-+ }
-+ dc_result[ch_idx][alc_idx][0] = idxf[0];
-+ dc_result[ch_idx][alc_idx][1] = idxf[1];
-+}
-+
-+static void rt2800_iq_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 *ges, u8 *pes)
-+{
-+ u32 p0 = 0, p1 = 0, pf = 0;
-+ char perr = 0, gerr = 0, iq_err = 0;
-+ char pef = 0, gef = 0;
-+ char psta, pend;
-+ char gsta, gend;
-+
-+ u8 ibit = 0x20;
-+ u8 first_search = 0x00, touch_neg_max = 0x00;
-+ char idx0 = 0, idx1 = 0;
-+ u8 gop;
-+ u8 bbp = 0;
-+ char bidx;
-+
-+ for (bidx = 5; bidx >= 1; bidx--) {
-+ for (gop = 0; gop < 2; gop++) {
-+ if (gop == 1 || bidx < 4) {
-+ if (gop == 0)
-+ iq_err = gerr;
-+ else
-+ iq_err = perr;
-+
-+ first_search = (gop == 0) ? (bidx == 3) : (bidx == 5);
-+ touch_neg_max = (gop) ? ((iq_err & 0x0F) == 0x08) :
-+ ((iq_err & 0x3F) == 0x20);
-+
-+ if (touch_neg_max) {
-+ p0 = pf;
-+ idx0 = iq_err;
-+ } else {
-+ idx0 = iq_err - ibit;
-+ bbp = (ch_idx == 0) ? ((gop == 0) ? 0x28 : 0x29) :
-+ ((gop == 0) ? 0x46 : 0x47);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, idx0);
-+
-+ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1);
-+ }
-+
-+ idx1 = iq_err + (first_search ? 0 : ibit);
-+ idx1 = (gop == 0) ? (idx1 & 0x0F) : (idx1 & 0x3F);
-+
-+ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 :
-+ (gop == 0) ? 0x46 : 0x47;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, idx1);
-+
-+ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1);
-+
-+ rt2x00_dbg(rt2x00dev,
-+ "p0=%x, p1=%x, pwer_final=%x, idx0=%x, idx1=%x, iq_err=%x, gop=%d, ibit=%x\n",
-+ p0, p1, pf, idx0, idx1, iq_err, gop, ibit);
-+
-+ if (!(!first_search && pf <= p0 && pf < p1)) {
-+ if (p0 < p1) {
-+ pf = p0;
-+ iq_err = idx0;
-+ } else {
-+ pf = p1;
-+ iq_err = idx1;
-+ }
-+ }
-+
-+ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 :
-+ (gop == 0) ? 0x46 : 0x47;
-+
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, iq_err);
-+
-+ if (gop == 0)
-+ gerr = iq_err;
-+ else
-+ perr = iq_err;
-+
-+ rt2x00_dbg(rt2x00dev, "IQCalibration pf=%8x (%2x, %2x) !\n",
-+ pf, gerr & 0x0F, perr & 0x3F);
-+ }
-+ }
-+
-+ if (bidx > 0)
-+ ibit = (ibit >> 1);
-+ }
-+ gerr = (gerr & 0x08) ? (gerr & 0x0F) - 0x10 : (gerr & 0x0F);
-+ perr = (perr & 0x20) ? (perr & 0x3F) - 0x40 : (perr & 0x3F);
-+
-+ gerr = (gerr < -0x07) ? -0x07 : (gerr > 0x05) ? 0x05 : gerr;
-+ gsta = gerr - 1;
-+ gend = gerr + 2;
-+
-+ perr = (perr < -0x1f) ? -0x1f : (perr > 0x1d) ? 0x1d : perr;
-+ psta = perr - 1;
-+ pend = perr + 2;
-+
-+ for (gef = gsta; gef <= gend; gef = gef + 1)
-+ for (pef = psta; pef <= pend; pef = pef + 1) {
-+ bbp = (ch_idx == 0) ? 0x28 : 0x46;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, gef & 0x0F);
-+
-+ bbp = (ch_idx == 0) ? 0x29 : 0x47;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, pef & 0x3F);
-+
-+ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1);
-+ if (gef == gsta && pef == psta) {
-+ pf = p1;
-+ gerr = gef;
-+ perr = pef;
-+ } else if (pf > p1) {
-+ pf = p1;
-+ gerr = gef;
-+ perr = pef;
-+ }
-+ rt2x00_dbg(rt2x00dev, "Fine IQCalibration p1=%8x pf=%8x (%2x, %2x) !\n",
-+ p1, pf, gef & 0x0F, pef & 0x3F);
-+ }
-+
-+ ges[ch_idx] = gerr & 0x0F;
-+ pes[ch_idx] = perr & 0x3F;
-+}
-+
-+static void rt2800_rf_aux_tx0_loopback(struct rt2x00_dev *rt2x00dev)
-+{
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x21);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, 0x10);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x1b);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 4, 0, 0x81);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 4, 2, 0x81);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 4, 34, 0xee);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, 0x2d);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, 0x2d);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, 0x80);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0xd7);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0xa2);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x20);
-+}
-+
-+static void rt2800_rf_aux_tx1_loopback(struct rt2x00_dev *rt2x00dev)
-+{
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 1, 0x22);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, 0x20);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x00);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x4b);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 0, 0x81);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 2, 0x81);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 6, 34, 0xee);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 3, 0x2d);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 4, 0x2d);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 17, 0x80);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 18, 0xd7);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 19, 0xa2);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 7, 20, 0x20);
-+}
-+
-+static void rt2800_loft_iq_calibration(struct rt2x00_dev *rt2x00dev)
-+{
-+ struct rf_reg_pair rf_store[CHAIN_NUM][13];
-+ u32 macorg1 = 0;
-+ u32 macorg2 = 0;
-+ u32 macorg3 = 0;
-+ u32 macorg4 = 0;
-+ u32 macorg5 = 0;
-+ u32 orig528 = 0;
-+ u32 orig52c = 0;
-+
-+ u32 savemacsysctrl = 0;
-+ u32 macvalue = 0;
-+ u32 mac13b8 = 0;
-+ u32 p0 = 0, p1 = 0;
-+ u32 p0_idx10 = 0, p1_idx10 = 0;
-+
-+ u8 rfvalue;
-+ u8 loft_dc_search_result[CHAIN_NUM][RF_ALC_NUM][2];
-+ u8 ger[CHAIN_NUM], per[CHAIN_NUM];
-+
-+ u8 vga_gain[] = {14, 14};
-+ u8 bbp = 0, ch_idx = 0, rf_alc_idx = 0, idx = 0;
-+ u8 bbpr30, rfb0r39, rfb0r42;
-+ u8 bbpr1;
-+ u8 bbpr4;
-+ u8 bbpr241, bbpr242;
-+ u8 count_step;
-+
-+ static const u8 rf_gain[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x0c};
-+ static const u8 rfvga_gain_table[] = {0x24, 0x25, 0x26, 0x27, 0x28, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
-+ 0x31, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3F};
-+ static const u8 bbp_2324gain[] = {0x16, 0x14, 0x12, 0x10, 0x0c, 0x08};
-+
-+ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG);
-+ macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0);
-+ macorg3 = rt2800_register_read(rt2x00dev, RF_BYPASS0);
-+ macorg4 = rt2800_register_read(rt2x00dev, RF_CONTROL3);
-+ macorg5 = rt2800_register_read(rt2x00dev, RF_BYPASS3);
-+ mac13b8 = rt2800_register_read(rt2x00dev, 0x13b8);
-+ orig528 = rt2800_register_read(rt2x00dev, RF_CONTROL2);
-+ orig52c = rt2800_register_read(rt2x00dev, RF_BYPASS2);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macvalue &= (~0x04);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX)))
-+ rt2x00_warn(rt2x00dev, "RF TX busy in LOFT IQ calibration\n");
-+
-+ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macvalue &= (~0x08);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX)))
-+ rt2x00_warn(rt2x00dev, "RF RX busy in LOFT IQ calibration\n");
-+
-+ for (ch_idx = 0; ch_idx < 2; ch_idx++)
-+ rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx);
-+
-+ bbpr30 = rt2800_bbp_read(rt2x00dev, 30);
-+ rfb0r39 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 39);
-+ rfb0r42 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 42);
-+
-+ rt2800_bbp_write(rt2x00dev, 30, 0x1F);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 39, 0x80);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, 0x5B);
-+
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+
-+ rt2800_setbbptonegenerator(rt2x00dev);
-+
-+ for (ch_idx = 0; ch_idx < 2; ch_idx++) {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00);
-+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x0000000F);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003306);
-+ rt2800_register_write(rt2x00dev, 0x13b8, 0x10);
-+ udelay(1);
-+
-+ if (ch_idx == 0)
-+ rt2800_rf_aux_tx0_loopback(rt2x00dev);
-+ else
-+ rt2800_rf_aux_tx1_loopback(rt2x00dev);
-+
-+ udelay(1);
-+
-+ if (ch_idx == 0)
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001004);
-+ else
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002004);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x05);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x01);
-+ if (ch_idx == 0)
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+ else
-+ rt2800_bbp_write(rt2x00dev, 159, 0x01);
-+
-+ vga_gain[ch_idx] = 18;
-+ for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) {
-+ rt2800_bbp_write(rt2x00dev, 23, bbp_2324gain[rf_alc_idx]);
-+ rt2800_bbp_write(rt2x00dev, 24, bbp_2324gain[rf_alc_idx]);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, RF_CONTROL3);
-+ macvalue &= (~0x0000F1F1);
-+ macvalue |= (rf_gain[rf_alc_idx] << 4);
-+ macvalue |= (rf_gain[rf_alc_idx] << 12);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, macvalue);
-+ macvalue = (0x0000F1F1);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, macvalue);
-+
-+ if (rf_alc_idx == 0) {
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x21);
-+ for (; vga_gain[ch_idx] > 0;
-+ vga_gain[ch_idx] = vga_gain[ch_idx] - 2) {
-+ rfvalue = rfvga_gain_table[vga_gain[ch_idx]];
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue);
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x00);
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x00);
-+ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0);
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x21);
-+ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0);
-+ rt2x00_dbg(rt2x00dev, "LOFT AGC %d %d\n", p0, p1);
-+ if ((p0 < 7000 * 7000) && (p1 < (7000 * 7000)))
-+ break;
-+ }
-+
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x00);
-+ rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x00);
-+
-+ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n", vga_gain[ch_idx],
-+ rfvga_gain_table[vga_gain[ch_idx]]);
-+
-+ if (vga_gain[ch_idx] < 0)
-+ vga_gain[ch_idx] = 0;
-+ }
-+
-+ rfvalue = rfvga_gain_table[vga_gain[ch_idx]];
-+
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue);
-+
-+ rt2800_loft_search(rt2x00dev, ch_idx, rf_alc_idx, loft_dc_search_result);
-+ }
-+ }
-+
-+ for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) {
-+ for (idx = 0; idx < 4; idx++) {
-+ rt2800_bbp_write(rt2x00dev, 158, 0xB0);
-+ bbp = (idx << 2) + rf_alc_idx;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+ rt2x00_dbg(rt2x00dev, " ALC %2x,", bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb1);
-+ bbp = loft_dc_search_result[CHAIN_0][rf_alc_idx][0x00];
-+ bbp = bbp & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+ rt2x00_dbg(rt2x00dev, " I0 %2x,", bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb2);
-+ bbp = loft_dc_search_result[CHAIN_0][rf_alc_idx][0x01];
-+ bbp = bbp & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+ rt2x00_dbg(rt2x00dev, " Q0 %2x,", bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb8);
-+ bbp = loft_dc_search_result[CHAIN_1][rf_alc_idx][0x00];
-+ bbp = bbp & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+ rt2x00_dbg(rt2x00dev, " I1 %2x,", bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0xb9);
-+ bbp = loft_dc_search_result[CHAIN_1][rf_alc_idx][0x01];
-+ bbp = bbp & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+ rt2x00_dbg(rt2x00dev, " Q1 %2x\n", bbp);
-+ }
-+ }
-+
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ bbp = 0x00;
-+ rt2800_bbp_write(rt2x00dev, 244, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 21, 0x01);
-+ udelay(1);
-+ rt2800_bbp_write(rt2x00dev, 21, 0x00);
-+
-+ rt2800_rf_configrecover(rt2x00dev, rf_store);
-+
-+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, macorg1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, macorg2);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, macorg3);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, macorg4);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, macorg5);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL2, orig528);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS2, orig52c);
-+ rt2800_register_write(rt2x00dev, 0x13b8, mac13b8);
-+
-+ savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG);
-+ macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0);
-+ macorg3 = rt2800_register_read(rt2x00dev, RF_BYPASS0);
-+ macorg4 = rt2800_register_read(rt2x00dev, RF_CONTROL3);
-+ macorg5 = rt2800_register_read(rt2x00dev, RF_BYPASS3);
-+
-+ bbpr1 = rt2800_bbp_read(rt2x00dev, 1);
-+ bbpr4 = rt2800_bbp_read(rt2x00dev, 4);
-+ bbpr241 = rt2800_bbp_read(rt2x00dev, 241);
-+ bbpr242 = rt2800_bbp_read(rt2x00dev, 242);
-+ mac13b8 = rt2800_register_read(rt2x00dev, 0x13b8);
-+
-+ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macvalue &= (~0x04);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX)))
-+ rt2x00_warn(rt2x00dev, "RF TX busy in LOFT IQ calibration\n");
-+
-+ macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL);
-+ macvalue &= (~0x08);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue);
-+
-+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX)))
-+ rt2x00_warn(rt2x00dev, "RF RX busy in LOFT IQ calibration\n");
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x00000101);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0000F1F1);
-+ }
-+
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_bbp_write(rt2x00dev, 4, bbpr4 & (~0x18));
-+ rt2800_bbp_write(rt2x00dev, 21, 0x01);
-+ udelay(1);
-+ rt2800_bbp_write(rt2x00dev, 21, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 241, 0x14);
-+ rt2800_bbp_write(rt2x00dev, 242, 0x80);
-+ rt2800_bbp_write(rt2x00dev, 244, 0x31);
-+ } else {
-+ rt2800_setbbptonegenerator(rt2x00dev);
-+ }
-+
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00000004);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00003306);
-+ udelay(1);
-+
-+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x0000000F);
-+
-+ if (!test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x00000000);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, 0x0000F1F1);
-+ }
-+
-+ rt2800_register_write(rt2x00dev, 0x13b8, 0x00000010);
-+
-+ for (ch_idx = 0; ch_idx < 2; ch_idx++)
-+ rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx);
-+
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x3B);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x3B);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x03);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x60);
-+ rt2800_bbp_write(rt2x00dev, 158, 0xB0);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x80);
-+
-+ for (ch_idx = 0; ch_idx < 2; ch_idx++) {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+
-+ if (ch_idx == 0) {
-+ rt2800_bbp_write(rt2x00dev, 158, 0x01);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ bbp = bbpr1 & (~0x18);
-+ bbp = bbp | 0x00;
-+ rt2800_bbp_write(rt2x00dev, 1, bbp);
-+ }
-+ rt2800_rf_aux_tx0_loopback(rt2x00dev);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001004);
-+ } else {
-+ rt2800_bbp_write(rt2x00dev, 158, 0x01);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x01);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags)) {
-+ bbp = bbpr1 & (~0x18);
-+ bbp = bbp | 0x08;
-+ rt2800_bbp_write(rt2x00dev, 1, bbp);
-+ }
-+ rt2800_rf_aux_tx1_loopback(rt2x00dev);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002004);
-+ }
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x05);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x04);
-+
-+ bbp = (ch_idx == 0) ? 0x28 : 0x46;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x06);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x06);
-+ count_step = 1;
-+ } else {
-+ rt2800_bbp_write(rt2x00dev, 23, 0x1F);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x1F);
-+ count_step = 2;
-+ }
-+
-+ for (; vga_gain[ch_idx] < 19; vga_gain[ch_idx] = (vga_gain[ch_idx] + count_step)) {
-+ rfvalue = rfvga_gain_table[vga_gain[ch_idx]];
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue);
-+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue);
-+
-+ bbp = (ch_idx == 0) ? 0x29 : 0x47;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+ p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags))
-+ p0_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A);
-+
-+ bbp = (ch_idx == 0) ? 0x29 : 0x47;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x21);
-+ p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0);
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags))
-+ p1_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A);
-+
-+ rt2x00_dbg(rt2x00dev, "IQ AGC %d %d\n", p0, p1);
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2x00_dbg(rt2x00dev, "IQ AGC IDX 10 %d %d\n", p0_idx10, p1_idx10);
-+ if ((p0_idx10 > 7000 * 7000) || (p1_idx10 > 7000 * 7000)) {
-+ if (vga_gain[ch_idx] != 0)
-+ vga_gain[ch_idx] = vga_gain[ch_idx] - 1;
-+ break;
-+ }
-+ }
-+
-+ if ((p0 > 2500 * 2500) || (p1 > 2500 * 2500))
-+ break;
-+ }
-+
-+ if (vga_gain[ch_idx] > 18)
-+ vga_gain[ch_idx] = 18;
-+ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n", vga_gain[ch_idx],
-+ rfvga_gain_table[vga_gain[ch_idx]]);
-+
-+ bbp = (ch_idx == 0) ? 0x29 : 0x47;
-+ rt2800_bbp_write(rt2x00dev, 158, bbp);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ rt2800_iq_search(rt2x00dev, ch_idx, ger, per);
-+ }
-+
-+ rt2800_bbp_write(rt2x00dev, 23, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 24, 0x00);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x28);
-+ bbp = ger[CHAIN_0] & 0x0F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x29);
-+ bbp = per[CHAIN_0] & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x46);
-+ bbp = ger[CHAIN_1] & 0x0F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x47);
-+ bbp = per[CHAIN_1] & 0x3F;
-+ rt2800_bbp_write(rt2x00dev, 159, bbp);
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) {
-+ rt2800_bbp_write(rt2x00dev, 1, bbpr1);
-+ rt2800_bbp_write(rt2x00dev, 241, bbpr241);
-+ rt2800_bbp_write(rt2x00dev, 242, bbpr242);
-+ }
-+ rt2800_bbp_write(rt2x00dev, 244, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 158, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+ rt2800_bbp_write(rt2x00dev, 158, 0xB0);
-+ rt2800_bbp_write(rt2x00dev, 159, 0x00);
-+
-+ rt2800_bbp_write(rt2x00dev, 30, bbpr30);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 39, rfb0r39);
-+ rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfb0r42);
-+
-+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags))
-+ rt2800_bbp_write(rt2x00dev, 4, bbpr4);
-+
-+ rt2800_bbp_write(rt2x00dev, 21, 0x01);
-+ udelay(1);
-+ rt2800_bbp_write(rt2x00dev, 21, 0x00);
-+
-+ rt2800_rf_configrecover(rt2x00dev, rf_store);
-+
-+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, macorg1);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x00);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL0, macorg2);
-+ udelay(1);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS0, macorg3);
-+ rt2800_register_write(rt2x00dev, RF_CONTROL3, macorg4);
-+ rt2800_register_write(rt2x00dev, RF_BYPASS3, macorg5);
-+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl);
-+ rt2800_register_write(rt2x00dev, 0x13b8, mac13b8);
-+}
-+
- static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev,
- bool set_bw, bool is_ht40)
- {
-@@ -9678,6 +10579,7 @@ static void rt2800_init_rfcsr_6352(struc
- rt2800_rxdcoc_calibration(rt2x00dev);
- rt2800_bw_filter_calibration(rt2x00dev, true);
- rt2800_bw_filter_calibration(rt2x00dev, false);
-+ rt2800_loft_iq_calibration(rt2x00dev);
- rt2800_rxiq_calibration(rt2x00dev);
- }
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
-@@ -17,6 +17,16 @@
- #define WCID_START 33
- #define WCID_END 222
- #define STA_IDS_SIZE (WCID_END - WCID_START + 2)
-+#define CHAIN_0 0x0
-+#define CHAIN_1 0x1
-+#define RF_ALC_NUM 6
-+#define CHAIN_NUM 2
-+
-+struct rf_reg_pair {
-+ u8 bank;
-+ u8 reg;
-+ u8 value;
-+};
-
- /* RT2800 driver data structure */
- struct rt2800_drv_data {
+++ /dev/null
-From patchwork Sat Sep 17 20:28:58 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979252
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:28:58 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 11/16] rt2x00: move helper functions up in file
-Message-ID:
- <c27baa8efd5c29e2bcb2432925d9cdc5c913a125.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-Move register access helper functions up to the head of the file so
-they can be used in all functions.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- .../net/wireless/ralink/rt2x00/rt2800lib.c | 40 +++++++++----------
- 1 file changed, 20 insertions(+), 20 deletions(-)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -198,6 +198,26 @@ static void rt2800_rfcsr_write_dccal(str
- rt2800_rfcsr_write_bank(rt2x00dev, 7, reg, value);
- }
-
-+static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev,
-+ const u8 reg, const u8 value)
-+{
-+ rt2800_bbp_write(rt2x00dev, 158, reg);
-+ rt2800_bbp_write(rt2x00dev, 159, value);
-+}
-+
-+static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg)
-+{
-+ rt2800_bbp_write(rt2x00dev, 158, reg);
-+ return rt2800_bbp_read(rt2x00dev, 159);
-+}
-+
-+static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev,
-+ const u8 reg, const u8 value)
-+{
-+ rt2800_bbp_write(rt2x00dev, 195, reg);
-+ rt2800_bbp_write(rt2x00dev, 196, value);
-+}
-+
- static u8 rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int word)
- {
-@@ -6972,26 +6992,6 @@ static void rt2800_init_bbp_5592(struct
- rt2800_bbp_write(rt2x00dev, 103, 0xc0);
- }
-
--static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev,
-- const u8 reg, const u8 value)
--{
-- rt2800_bbp_write(rt2x00dev, 195, reg);
-- rt2800_bbp_write(rt2x00dev, 196, value);
--}
--
--static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev,
-- const u8 reg, const u8 value)
--{
-- rt2800_bbp_write(rt2x00dev, 158, reg);
-- rt2800_bbp_write(rt2x00dev, 159, value);
--}
--
--static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg)
--{
-- rt2800_bbp_write(rt2x00dev, 158, reg);
-- return rt2800_bbp_read(rt2x00dev, 159);
--}
--
- static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev)
- {
- u8 bbp;
+++ /dev/null
-From patchwork Sat Sep 17 20:29:13 2022
-Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
-X-Patchwork-Id: 12979253
-X-Patchwork-Delegate: kvalo@adurom.com
-Return-Path: <linux-wireless-owner@kernel.org>
-Date: Sat, 17 Sep 2022 21:29:13 +0100
-From: Daniel Golle <daniel@makrotopia.org>
-To: linux-wireless@vger.kernel.org, Stanislaw Gruszka <stf_xl@wp.pl>,
- Helmut Schaa <helmut.schaa@googlemail.com>
-Cc: Kalle Valo <kvalo@kernel.org>,
- "David S. Miller" <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Johannes Berg <johannes.berg@intel.com>
-Subject: [PATCH v3 12/16] rt2x00: fix HT20/HT40 bandwidth switch on MT7620
-Message-ID:
- <1664d89ba149f7b0bcec18b2a2abaedf49654507.1663445157.git.daniel@makrotopia.org>
-References: <cover.1663445157.git.daniel@makrotopia.org>
-MIME-Version: 1.0
-Content-Disposition: inline
-In-Reply-To: <cover.1663445157.git.daniel@makrotopia.org>
-Precedence: bulk
-List-ID: <linux-wireless.vger.kernel.org>
-X-Mailing-List: linux-wireless@vger.kernel.org
-
-Add missing configuration of the channel bandwidth filter to the
-channel setup function for MT7620.
-
-Reported-by: Serge Vasilugin <vasilugin@yandex.ru>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
----
- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
-@@ -3855,6 +3855,14 @@ static void rt2800_config_channel_rf7620
- rfcsr |= tx_agc_fc;
- rt2800_rfcsr_write_bank(rt2x00dev, 7, 59, rfcsr);
- }
-+
-+ if (conf_is_ht40(conf)) {
-+ rt2800_bbp_glrt_write(rt2x00dev, 141, 0x10);
-+ rt2800_bbp_glrt_write(rt2x00dev, 157, 0x2f);
-+ } else {
-+ rt2800_bbp_glrt_write(rt2x00dev, 141, 0x1a);
-+ rt2800_bbp_glrt_write(rt2x00dev, 157, 0x40);
-+ }
- }
-
- static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev,
--- a/local-symbols
+++ b/local-symbols
-@@ -345,6 +345,7 @@ RT2X00_LIB_FIRMWARE=
+@@ -354,6 +354,7 @@ RT2X00_LIB_FIRMWARE=
RT2X00_LIB_CRYPTO=
RT2X00_LIB_LEDS=
RT2X00_LIB_DEBUGFS=
/* Firmware functions */
static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev)
{
-@@ -167,7 +154,6 @@ static const struct rt2800_ops rt2800soc
+@@ -168,7 +155,6 @@ static const struct rt2800_ops rt2800soc
.register_multiread = rt2x00mmio_register_multiread,
.register_multiwrite = rt2x00mmio_register_multiwrite,
.regbusy_read = rt2x00mmio_regbusy_read,
--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-@@ -224,10 +224,17 @@ static int rt2800soc_probe(struct platfo
+@@ -225,10 +225,17 @@ static int rt2800soc_probe(struct platfo
return rt2x00soc_probe(pdev, &rt2800soc_ops);
}
+
static const struct ieee80211_ops rt2800pci_mac80211_ops = {
.tx = rt2x00mac_tx,
- .start = rt2x00mac_start,
-@@ -328,6 +332,9 @@ static const struct rt2800_ops rt2800pci
+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+@@ -329,6 +333,9 @@ static const struct rt2800_ops rt2800pci
.drv_init_registers = rt2800mmio_init_registers,
.drv_get_txwi = rt2800mmio_get_txwi,
.drv_get_dma_done = rt2800mmio_get_dma_done,
+
static const struct ieee80211_ops rt2800soc_mac80211_ops = {
.tx = rt2x00mac_tx,
- .start = rt2x00mac_start,
-@@ -159,6 +186,9 @@ static const struct rt2800_ops rt2800soc
+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+@@ -160,6 +187,9 @@ static const struct rt2800_ops rt2800soc
.drv_init_registers = rt2800mmio_init_registers,
.drv_get_txwi = rt2800mmio_get_txwi,
.drv_get_dma_done = rt2800mmio_get_dma_done,
+
static const struct ieee80211_ops rt2800usb_mac80211_ops = {
.tx = rt2x00mac_tx,
- .start = rt2x00mac_start,
-@@ -671,6 +675,9 @@ static const struct rt2800_ops rt2800usb
+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+@@ -672,6 +676,9 @@ static const struct rt2800_ops rt2800usb
.drv_init_registers = rt2800usb_init_registers,
.drv_get_txwi = rt2800usb_get_txwi,
.drv_get_dma_done = rt2800usb_get_dma_done,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -1319,7 +1319,6 @@ static int ieee80211_stop_ap(struct wiph
- sdata->vif.bss_conf.ftmr_params = NULL;
+@@ -1512,7 +1512,6 @@ static int ieee80211_stop_ap(struct wiph
+ link_conf->ftmr_params = NULL;
__sta_info_flush(sdata, true);
- ieee80211_free_keys(sdata, true);
- sdata->vif.bss_conf.enable_beacon = false;
+ link_conf->enable_beacon = false;
sdata->beacon_rate_set = false;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 24 Feb 2013 00:00:00 +0100
-Subject: [PATCH] mac80211: disable ipv4/ipv6 address notifiers
-
----
- net/mac80211/main.c | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -337,7 +337,7 @@ void ieee80211_restart_hw(struct ieee802
- }
- EXPORT_SYMBOL(ieee80211_restart_hw);
-
--#ifdef CONFIG_INET
-+#ifdef __disabled__CONFIG_INET
- static int ieee80211_ifa_changed(struct notifier_block *nb,
- unsigned long data, void *arg)
- {
-@@ -396,7 +396,7 @@ static int ieee80211_ifa_changed(struct
- }
- #endif
-
--#if IS_ENABLED(CONFIG_IPV6)
-+#if IS_ENABLED(__disabled__CONFIG_IPV6)
- static int ieee80211_ifa6_changed(struct notifier_block *nb,
- unsigned long data, void *arg)
- {
-@@ -1321,14 +1321,14 @@ int ieee80211_register_hw(struct ieee802
- wiphy_unlock(hw->wiphy);
- rtnl_unlock();
-
--#ifdef CONFIG_INET
-+#ifdef __disabled__CONFIG_INET
- local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
- result = register_inetaddr_notifier(&local->ifa_notifier);
- if (result)
- goto fail_ifa;
- #endif
-
--#if IS_ENABLED(CONFIG_IPV6)
-+#if IS_ENABLED(__disabled__CONFIG_IPV6)
- local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
- result = register_inet6addr_notifier(&local->ifa6_notifier);
- if (result)
-@@ -1337,13 +1337,13 @@ int ieee80211_register_hw(struct ieee802
-
- return 0;
-
--#if IS_ENABLED(CONFIG_IPV6)
-+#if IS_ENABLED(__disabled__CONFIG_IPV6)
- fail_ifa6:
--#ifdef CONFIG_INET
-+#ifdef __disabled__CONFIG_INET
- unregister_inetaddr_notifier(&local->ifa_notifier);
- #endif
- #endif
--#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
-+#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6)
- fail_ifa:
- #endif
- wiphy_unregister(local->hw.wiphy);
-@@ -1373,10 +1373,10 @@ void ieee80211_unregister_hw(struct ieee
- tasklet_kill(&local->tx_pending_tasklet);
- tasklet_kill(&local->tasklet);
-
--#ifdef CONFIG_INET
-+#ifdef __disabled__CONFIG_INET
- unregister_inetaddr_notifier(&local->ifa_notifier);
- #endif
--#if IS_ENABLED(CONFIG_IPV6)
-+#if IS_ENABLED(__disabled__CONFIG_IPV6)
- unregister_inet6addr_notifier(&local->ifa6_notifier);
- #endif
-
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -2497,7 +2497,7 @@ static int ieee80211_scan(struct wiphy *
- * the frames sent while scanning on other channel will be
- * lost)
+@@ -2720,6 +2720,8 @@ static int ieee80211_scan(struct wiphy *
*/
-- if (sdata->u.ap.beacon &&
-+ if (0 && sdata->u.ap.beacon &&
- (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
- !(req->flags & NL80211_SCAN_FLAG_AP)))
- return -EOPNOTSUPP;
+ fallthrough;
+ case NL80211_IFTYPE_AP:
++ /* skip check */
++ break;
+ /*
+ * If the scan has been forced (and the driver supports
+ * forcing), don't care about being beaconing already.
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
-@@ -357,6 +357,7 @@ struct sta_info *sta_info_alloc(struct i
+@@ -554,6 +554,7 @@ __sta_info_alloc(struct ieee80211_sub_if
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 28 Apr 2021 21:03:13 +0200
+Subject: [PATCH] mac80211: minstrel_ht: fix MINSTREL_FRAC macro
+
+Add missing braces to avoid issues with e.g. using additions in the
+div expression
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rc80211_minstrel_ht.h
++++ b/net/mac80211/rc80211_minstrel_ht.h
+@@ -14,7 +14,7 @@
+
+ /* scaled fraction values */
+ #define MINSTREL_SCALE 12
+-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
++#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
+ #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+
+ #define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 6 Feb 2021 16:08:01 +0100
+Subject: [PATCH] mac80211: minstrel_ht: reduce fluctuations in rate
+ probability stats
+
+In some scenarios when there is a lot of fluctuation in packet error rates,
+rate switching can be amplified when the statistics get skewed by time slots
+with very few tries.
+Make the input data to the moving average more smooth by adding the
+success/attempts count from the last stats window as well. This has the
+advantage of smoothing the data without introducing any extra lag to sampling
+rates.
+This significantly improves rate stability on a strong test link subjected to
+periodic noise bursts generated with a SDR
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rc80211_minstrel_ht.c
++++ b/net/mac80211/rc80211_minstrel_ht.c
+@@ -769,7 +769,8 @@ minstrel_ht_calc_rate_stats(struct minst
+ unsigned int cur_prob;
+
+ if (unlikely(mrs->attempts > 0)) {
+- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
++ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
++ mrs->attempts + mrs->last_attempts);
+ minstrel_filter_avg_add(&mrs->prob_avg,
+ &mrs->prob_avg_1, cur_prob);
+ mrs->att_hist += mrs->attempts;
+++ /dev/null
-From: Xing Song <xing.song@mediatek.com>
-Date: Tue, 23 Nov 2021 11:31:23 +0800
-Subject: [PATCH] mac80211: set up the fwd_skb->dev for mesh forwarding
-
-Mesh forwarding requires that the fwd_skb->dev is set up for TX handling,
-otherwise the following warning will be generated, so set it up for the
-pending frames.
-
-[ 72.835674 ] WARNING: CPU: 0 PID: 1193 at __skb_flow_dissect+0x284/0x1298
-[ 72.842379 ] Modules linked in: ksmbd pppoe ppp_async l2tp_ppp ...
-[ 72.962020 ] CPU: 0 PID: 1193 Comm: kworker/u5:1 Tainted: P S 5.4.137 #0
-[ 72.969938 ] Hardware name: MT7622_MT7531 RFB (DT)
-[ 72.974659 ] Workqueue: napi_workq napi_workfn
-[ 72.979025 ] pstate: 60000005 (nZCv daif -PAN -UAO)
-[ 72.983822 ] pc : __skb_flow_dissect+0x284/0x1298
-[ 72.988444 ] lr : __skb_flow_dissect+0x54/0x1298
-[ 72.992977 ] sp : ffffffc010c738c0
-[ 72.996293 ] x29: ffffffc010c738c0 x28: 0000000000000000
-[ 73.001615 ] x27: 000000000000ffc2 x26: ffffff800c2eb818
-[ 73.006937 ] x25: ffffffc010a987c8 x24: 00000000000000ce
-[ 73.012259 ] x23: ffffffc010c73a28 x22: ffffffc010a99c60
-[ 73.017581 ] x21: 000000000000ffc2 x20: ffffff80094da800
-[ 73.022903 ] x19: 0000000000000000 x18: 0000000000000014
-[ 73.028226 ] x17: 00000000084d16af x16: 00000000d1fc0bab
-[ 73.033548 ] x15: 00000000715f6034 x14: 000000009dbdd301
-[ 73.038870 ] x13: 00000000ea4dcbc3 x12: 0000000000000040
-[ 73.044192 ] x11: 000000000eb00ff0 x10: 0000000000000000
-[ 73.049513 ] x9 : 000000000eb00073 x8 : 0000000000000088
-[ 73.054834 ] x7 : 0000000000000000 x6 : 0000000000000001
-[ 73.060155 ] x5 : 0000000000000000 x4 : 0000000000000000
-[ 73.065476 ] x3 : ffffffc010a98000 x2 : 0000000000000000
-[ 73.070797 ] x1 : 0000000000000000 x0 : 0000000000000000
-[ 73.076120 ] Call trace:
-[ 73.078572 ] __skb_flow_dissect+0x284/0x1298
-[ 73.082846 ] __skb_get_hash+0x7c/0x228
-[ 73.086629 ] ieee80211_txq_may_transmit+0x7fc/0x17b8 [mac80211]
-[ 73.092564 ] ieee80211_tx_prepare_skb+0x20c/0x268 [mac80211]
-[ 73.098238 ] ieee80211_tx_pending+0x144/0x330 [mac80211]
-[ 73.103560 ] tasklet_action_common.isra.16+0xb4/0x158
-[ 73.108618 ] tasklet_action+0x2c/0x38
-[ 73.112286 ] __do_softirq+0x168/0x3b0
-[ 73.115954 ] do_softirq.part.15+0x88/0x98
-[ 73.119969 ] __local_bh_enable_ip+0xb0/0xb8
-[ 73.124156 ] napi_workfn+0x58/0x90
-[ 73.127565 ] process_one_work+0x20c/0x478
-[ 73.131579 ] worker_thread+0x50/0x4f0
-[ 73.135249 ] kthread+0x124/0x128
-[ 73.138484 ] ret_from_fork+0x10/0x1c
-
-Signed-off-by: Xing Song <xing.song@mediatek.com>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2950,6 +2950,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
- if (!fwd_skb)
- goto out;
-
-+ fwd_skb->dev = sdata->dev;
- fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
- fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
- info = IEEE80211_SKB_CB(fwd_skb);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 6 Feb 2021 16:33:14 +0100
+Subject: [PATCH] mac80211: minstrel_ht: rework rate downgrade code and
+ max_prob rate selection
+
+The current fallback code for fast rate switching on potentially failing rates
+is triggering too often if there is some strong noise on the channel. This can
+lead to wild fluctuations in the rate selection.
+Additionally, switching down to max_prob_rate can create a significant gap down
+in throughput, especially when using only 2 spatial streams, because max_prob_rate
+is limited to using fewer streams than the max_tp rates.
+In order to improve throughput without reducing reliability too much, use the
+rate downgrade code for the max_prob_rate only, and allow the non-downgraded
+max_prob_rate to use as many spatial streams as the max_tp rates
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rc80211_minstrel_ht.c
++++ b/net/mac80211/rc80211_minstrel_ht.c
+@@ -580,6 +580,14 @@ minstrel_ht_set_best_prob_rate(struct mi
+ int cur_tp_avg, cur_group, cur_idx;
+ int max_gpr_group, max_gpr_idx;
+ int max_gpr_tp_avg, max_gpr_prob;
++ int min_dur;
++
++ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
++ minstrel_get_duration(mi->max_tp_rate[1]));
++
++ /* make the rate at least 18% slower than max tp rates */
++ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
++ return;
+
+ cur_group = MI_RATE_GROUP(index);
+ cur_idx = MI_RATE_IDX(index);
+@@ -601,11 +609,6 @@ minstrel_ht_set_best_prob_rate(struct mi
+ !minstrel_ht_is_legacy_group(max_tp_group))
+ return;
+
+- /* skip rates faster than max tp rate with lower prob */
+- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
+- mrs->prob_avg < max_tp_prob)
+- return;
+-
+ max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
+ max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+ max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
+@@ -663,40 +666,6 @@ minstrel_ht_assign_best_tp_rates(struct
+
+ }
+
+-/*
+- * Try to increase robustness of max_prob rate by decrease number of
+- * streams if possible.
+- */
+-static inline void
+-minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
+-{
+- struct minstrel_mcs_group_data *mg;
+- int tmp_max_streams, group, tmp_idx, tmp_prob;
+- int tmp_tp = 0;
+-
+- if (!mi->sta->deflink.ht_cap.ht_supported)
+- return;
+-
+- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
+- tmp_max_streams = minstrel_mcs_groups[group].streams;
+- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+- mg = &mi->groups[group];
+- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
+- continue;
+-
+- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
+- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
+-
+- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
+- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
+- mi->max_prob_rate = mg->max_group_prob_rate;
+- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+- tmp_idx,
+- tmp_prob);
+- }
+- }
+-}
+-
+ static u16
+ __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
+ enum minstrel_sample_type type)
+@@ -1176,8 +1145,6 @@ minstrel_ht_update_stats(struct minstrel
+
+ mi->max_prob_rate = tmp_max_prob_rate;
+
+- /* Try to increase robustness of max_prob_rate*/
+- minstrel_ht_prob_rate_reduce_streams(mi);
+ minstrel_ht_refill_sample_rates(mi);
+
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+@@ -1256,7 +1223,7 @@ minstrel_ht_ri_txstat_valid(struct minst
+ }
+
+ static void
+-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
++minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
+ {
+ int group, orig_group;
+
+@@ -1271,11 +1238,7 @@ minstrel_downgrade_rate(struct minstrel_
+ minstrel_mcs_groups[orig_group].streams)
+ continue;
+
+- if (primary)
+- *idx = mi->groups[group].max_group_tp_rate[0];
+- else
+- *idx = mi->groups[group].max_group_tp_rate[1];
+- break;
++ *idx = mi->groups[group].max_group_prob_rate;
+ }
+ }
+
+@@ -1286,7 +1249,7 @@ minstrel_ht_tx_status(void *priv, struct
+ struct ieee80211_tx_info *info = st->info;
+ struct minstrel_ht_sta *mi = priv_sta;
+ struct ieee80211_tx_rate *ar = info->status.rates;
+- struct minstrel_rate_stats *rate, *rate2;
++ struct minstrel_rate_stats *rate;
+ struct minstrel_priv *mp = priv;
+ u32 update_interval = mp->update_interval;
+ bool last, update = false;
+@@ -1354,18 +1317,13 @@ minstrel_ht_tx_status(void *priv, struct
+ /*
+ * check for sudden death of spatial multiplexing,
+ * downgrade to a lower number of streams if necessary.
++ * only do this for the max_prob_rate to prevent spurious
++ * rate fluctuations when the link changes suddenly
+ */
+- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
++ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
+ if (rate->attempts > 30 &&
+ rate->success < rate->attempts / 4) {
+- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
+- update = true;
+- }
+-
+- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
+- if (rate2->attempts > 30 &&
+- rate2->success < rate2->attempts / 4) {
+- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
++ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
+ update = true;
+ }
+ }
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 26 Jun 2022 11:43:25 +0200
+Subject: [PATCH] mac80211: increase quantum for airtime scheduler
+
+Given the typical AQL budget and queue length, a quantum of 256 with the
+default station weight often requires iterating over all queues frequently,
+until one of them becomes eligible.
+Improve performance by using 8 times station weight as scheduler quantum
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -90,6 +90,8 @@ extern const u8 ieee80211_ac_to_qos_mask
+ */
+ #define AIRTIME_ACTIVE_DURATION (HZ / 10)
+
++#define AIRTIME_QUANTUM_SHIFT 3
++
+ struct ieee80211_bss {
+ u32 device_ts_beacon, device_ts_presp;
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3976,7 +3976,7 @@ struct ieee80211_txq *ieee80211_next_txq
+
+ if (deficit < 0)
+ sta->airtime[txqi->txq.ac].deficit +=
+- sta->airtime_weight;
++ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
+
+ if (deficit < 0 || !aql_check) {
+ list_move_tail(&txqi->schedule_order,
+@@ -4119,7 +4119,8 @@ bool ieee80211_txq_may_transmit(struct i
+ }
+ sta = container_of(iter->txq.sta, struct sta_info, sta);
+ if (ieee80211_sta_deficit(sta, ac) < 0)
+- sta->airtime[ac].deficit += sta->airtime_weight;
++ sta->airtime[ac].deficit += sta->airtime_weight <<
++ AIRTIME_QUANTUM_SHIFT;
+ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
+ }
+
+@@ -4127,7 +4128,7 @@ bool ieee80211_txq_may_transmit(struct i
+ if (sta->airtime[ac].deficit >= 0)
+ goto out;
+
+- sta->airtime[ac].deficit += sta->airtime_weight;
++ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
+ list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
+ spin_unlock_bh(&local->active_txq_lock[ac]);
+
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:38 +0200
+Subject: [PATCH] wifi: mac80211: add internal handler for wake_tx_queue
+
+Start to align the TX handling to only use internal TX queues (iTXQs):
+
+Provide a handler for drivers not having a custom wake_tx_queue
+callback and update the documentation.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -89,15 +89,13 @@
+ /**
+ * DOC: mac80211 software tx queueing
+ *
+- * mac80211 provides an optional intermediate queueing implementation designed
+- * to allow the driver to keep hardware queues short and provide some fairness
+- * between different stations/interfaces.
+- * In this model, the driver pulls data frames from the mac80211 queue instead
+- * of letting mac80211 push them via drv_tx().
+- * Other frames (e.g. control or management) are still pushed using drv_tx().
++ * mac80211 uses an intermediate queueing implementation, designed to allow the
++ * driver to keep hardware queues short and to provide some fairness between
++ * different stations/interfaces.
+ *
+- * Drivers indicate that they use this model by implementing the .wake_tx_queue
+- * driver operation.
++ * Drivers must provide the .wake_tx_queue driver operation by either
++ * linking it to ieee80211_handle_wake_tx_queue() or implementing a custom
++ * handler.
+ *
+ * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with
+ * another per-sta for non-data/non-mgmt and bufferable management frames, and
+@@ -106,9 +104,12 @@
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+- * The driver can't access the queue directly. To dequeue a frame from a
+- * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a
+- * queue, it calls the .wake_tx_queue driver op.
++ * The driver can't access the internal TX queues (iTXQs) directly.
++ * Whenever mac80211 adds a new frame to a queue, it calls the .wake_tx_queue
++ * driver op.
++ * Drivers implementing a custom .wake_tx_queue op can get them by calling
++ * ieee80211_tx_dequeue(). Drivers using ieee80211_handle_wake_tx_queue() will
++ * simply get the individual frames pushed via the .tx driver operation.
+ *
+ * Drivers can optionally delegate responsibility for scheduling queues to
+ * mac80211, to take advantage of airtime fairness accounting. In this case, to
+@@ -1826,7 +1827,7 @@ struct ieee80211_vif_cfg {
+ * for this interface.
+ * @drv_priv: data area for driver use, will always be aligned to
+ * sizeof(void \*).
+- * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
++ * @txq: the multicast data TX queue
+ * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
+ * protected by fq->lock.
+ * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
+@@ -2252,8 +2253,8 @@ struct ieee80211_link_sta {
+ * For non MLO STA it will point to the deflink data. For MLO STA
+ * ieee80211_sta_recalc_aggregates() must be called to update it.
+ * @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not.
+- * @txq: per-TID data TX queues (if driver uses the TXQ abstraction); note that
+- * the last entry (%IEEE80211_NUM_TIDS) is used for non-data frames
++ * @txq: per-TID data TX queues; note that the last entry (%IEEE80211_NUM_TIDS)
++ * is used for non-data frames
+ * @deflink: This holds the default link STA information, for non MLO STA all link
+ * specific STA information is accessed through @deflink or through
+ * link[0] which points to address of @deflink. For MLO Link STA
+@@ -5691,7 +5692,7 @@ void ieee80211_key_replay(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_wake_queue.
++ * Drivers must use this function instead of netif_wake_queue.
+ */
+ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue);
+
+@@ -5700,7 +5701,7 @@ void ieee80211_wake_queue(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_stop_queue.
+ */
+ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
+
+@@ -5709,7 +5710,7 @@ void ieee80211_stop_queue(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_queue_stopped.
+ *
+ * Return: %true if the queue is stopped. %false otherwise.
+ */
+@@ -5720,7 +5721,7 @@ int ieee80211_queue_stopped(struct ieee8
+ * ieee80211_stop_queues - stop all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_tx_stop_all_queues.
+ */
+ void ieee80211_stop_queues(struct ieee80211_hw *hw);
+
+@@ -5728,7 +5729,7 @@ void ieee80211_stop_queues(struct ieee80
+ * ieee80211_wake_queues - wake all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+- * Drivers should use this function instead of netif_wake_queue.
++ * Drivers must use this function instead of netif_tx_wake_all_queues.
+ */
+ void ieee80211_wake_queues(struct ieee80211_hw *hw);
+
+@@ -6950,6 +6951,18 @@ static inline struct sk_buff *ieee80211_
+ }
+
+ /**
++ * ieee80211_handle_wake_tx_queue - mac80211 handler for wake_tx_queue callback
++ *
++ * @hw: pointer as obtained from wake_tx_queue() callback().
++ * @txq: pointer as obtained from wake_tx_queue() callback().
++ *
++ * Drivers can use this function for the mandatory mac80211 wake_tx_queue
++ * callback in struct ieee80211_ops. They should not call this function.
++ */
++void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
++ struct ieee80211_txq *txq);
++
++/**
+ * ieee80211_next_txq - get next tx queue to pull packets from
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -288,6 +288,52 @@ __le16 ieee80211_ctstoself_duration(stru
+ }
+ EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+
++static void wake_tx_push_queue(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_txq *queue)
++{
++ int q = sdata->vif.hw_queue[queue->ac];
++ struct ieee80211_tx_control control = {
++ .sta = queue->sta,
++ };
++ struct sk_buff *skb;
++ unsigned long flags;
++ bool q_stopped;
++
++ while (1) {
++ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++ q_stopped = local->queue_stop_reasons[q];
++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++
++ if (q_stopped)
++ break;
++
++ skb = ieee80211_tx_dequeue(&local->hw, queue);
++ if (!skb)
++ break;
++
++ drv_tx(local, &control, skb);
++ }
++}
++
++/* wake_tx_queue handler for driver not implementing a custom one*/
++void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
++ struct ieee80211_txq *txq)
++{
++ struct ieee80211_local *local = hw_to_local(hw);
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
++ struct ieee80211_txq *queue;
++
++ /* Use ieee80211_next_txq() for airtime fairness accounting */
++ ieee80211_txq_schedule_start(hw, txq->ac);
++ while ((queue = ieee80211_next_txq(hw, txq->ac))) {
++ wake_tx_push_queue(local, sdata, queue);
++ ieee80211_return_txq(hw, queue, false);
++ }
++ ieee80211_txq_schedule_end(hw, txq->ac);
++}
++EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
++
+ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
+ {
+ struct ieee80211_local *local = sdata->local;
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:39 +0200
+Subject: [PATCH] wifi: mac80211: add wake_tx_queue callback to drivers
+
+mac80211 is fully switching over to the internal TX queue (iTXQ)
+implementation. Update all drivers not yet providing the now mandatory
+wake_tx_queue() callback.
+
+As an side effect the netdev interfaces of all updated drivers will
+switch to the noqueue qdisc.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+[add staging drivers]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/admtek/adm8211.c
++++ b/drivers/net/wireless/admtek/adm8211.c
+@@ -1760,6 +1760,7 @@ static int adm8211_alloc_rings(struct ie
+
+ static const struct ieee80211_ops adm8211_ops = {
+ .tx = adm8211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = adm8211_start,
+ .stop = adm8211_stop,
+ .add_interface = adm8211_add_interface,
+--- a/drivers/net/wireless/ath/ar5523/ar5523.c
++++ b/drivers/net/wireless/ath/ar5523/ar5523.c
+@@ -1355,6 +1355,7 @@ static const struct ieee80211_ops ar5523
+ .start = ar5523_start,
+ .stop = ar5523_stop,
+ .tx = ar5523_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_rts_threshold = ar5523_set_rts_threshold,
+ .add_interface = ar5523_add_interface,
+ .remove_interface = ar5523_remove_interface,
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -8539,6 +8539,7 @@ err_fallback:
+
+ static const struct ieee80211_ops ath11k_ops = {
+ .tx = ath11k_mac_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath11k_mac_op_start,
+ .stop = ath11k_mac_op_stop,
+ .reconfig_complete = ath11k_mac_op_reconfig_complete,
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -781,6 +781,7 @@ static int ath5k_set_ringparam(struct ie
+
+ const struct ieee80211_ops ath5k_hw_ops = {
+ .tx = ath5k_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath5k_start,
+ .stop = ath5k_stop,
+ .add_interface = ath5k_add_interface,
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+@@ -1870,6 +1870,7 @@ static void ath9k_htc_channel_switch_bea
+
+ struct ieee80211_ops ath9k_htc_ops = {
+ .tx = ath9k_htc_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath9k_htc_start,
+ .stop = ath9k_htc_stop,
+ .add_interface = ath9k_htc_add_interface,
+--- a/drivers/net/wireless/ath/carl9170/main.c
++++ b/drivers/net/wireless/ath/carl9170/main.c
+@@ -1715,6 +1715,7 @@ static const struct ieee80211_ops carl91
+ .start = carl9170_op_start,
+ .stop = carl9170_op_stop,
+ .tx = carl9170_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .flush = carl9170_op_flush,
+ .add_interface = carl9170_op_add_interface,
+ .remove_interface = carl9170_op_remove_interface,
+--- a/drivers/net/wireless/ath/wcn36xx/main.c
++++ b/drivers/net/wireless/ath/wcn36xx/main.c
+@@ -1362,6 +1362,7 @@ static const struct ieee80211_ops wcn36x
+ .prepare_multicast = wcn36xx_prepare_multicast,
+ .configure_filter = wcn36xx_configure_filter,
+ .tx = wcn36xx_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wcn36xx_set_key,
+ .hw_scan = wcn36xx_hw_scan,
+ .cancel_hw_scan = wcn36xx_cancel_hw_scan,
+--- a/drivers/net/wireless/atmel/at76c50x-usb.c
++++ b/drivers/net/wireless/atmel/at76c50x-usb.c
+@@ -2187,6 +2187,7 @@ static int at76_set_key(struct ieee80211
+
+ static const struct ieee80211_ops at76_ops = {
+ .tx = at76_mac80211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = at76_add_interface,
+ .remove_interface = at76_remove_interface,
+ .config = at76_config,
+--- a/drivers/net/wireless/broadcom/b43/main.c
++++ b/drivers/net/wireless/broadcom/b43/main.c
+@@ -5171,6 +5171,7 @@ static int b43_op_get_survey(struct ieee
+
+ static const struct ieee80211_ops b43_hw_ops = {
+ .tx = b43_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .conf_tx = b43_op_conf_tx,
+ .add_interface = b43_op_add_interface,
+ .remove_interface = b43_op_remove_interface,
+--- a/drivers/net/wireless/broadcom/b43legacy/main.c
++++ b/drivers/net/wireless/broadcom/b43legacy/main.c
+@@ -3532,6 +3532,7 @@ static int b43legacy_op_get_survey(struc
+
+ static const struct ieee80211_ops b43legacy_hw_ops = {
+ .tx = b43legacy_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .conf_tx = b43legacy_op_conf_tx,
+ .add_interface = b43legacy_op_add_interface,
+ .remove_interface = b43legacy_op_remove_interface,
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
+@@ -962,6 +962,7 @@ static int brcms_ops_beacon_set_tim(stru
+
+ static const struct ieee80211_ops brcms_ops = {
+ .tx = brcms_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = brcms_ops_start,
+ .stop = brcms_ops_stop,
+ .add_interface = brcms_ops_add_interface,
+--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
++++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+@@ -3435,6 +3435,7 @@ static const struct attribute_group il39
+
+ static struct ieee80211_ops il3945_mac_ops __ro_after_init = {
+ .tx = il3945_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = il3945_mac_start,
+ .stop = il3945_mac_stop,
+ .add_interface = il_mac_add_interface,
+--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
++++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+@@ -6304,6 +6304,7 @@ il4965_tx_queue_set_status(struct il_pri
+
+ static const struct ieee80211_ops il4965_mac_ops = {
+ .tx = il4965_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = il4965_mac_start,
+ .stop = il4965_mac_stop,
+ .add_interface = il_mac_add_interface,
+--- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
++++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
+@@ -1571,6 +1571,7 @@ static void iwlagn_mac_sta_notify(struct
+
+ const struct ieee80211_ops iwlagn_hw_ops = {
+ .tx = iwlagn_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = iwlagn_mac_start,
+ .stop = iwlagn_mac_stop,
+ #ifdef CONFIG_PM_SLEEP
+--- a/drivers/net/wireless/intersil/p54/main.c
++++ b/drivers/net/wireless/intersil/p54/main.c
+@@ -705,6 +705,7 @@ static void p54_set_coverage_class(struc
+
+ static const struct ieee80211_ops p54_ops = {
+ .tx = p54_tx_80211,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = p54_start,
+ .stop = p54_stop,
+ .add_interface = p54_add_interface,
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -3109,6 +3109,7 @@ static int mac80211_hwsim_change_sta_lin
+
+ #define HWSIM_COMMON_OPS \
+ .tx = mac80211_hwsim_tx, \
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue, \
+ .start = mac80211_hwsim_start, \
+ .stop = mac80211_hwsim_stop, \
+ .add_interface = mac80211_hwsim_add_interface, \
+--- a/drivers/net/wireless/marvell/libertas_tf/main.c
++++ b/drivers/net/wireless/marvell/libertas_tf/main.c
+@@ -474,6 +474,7 @@ static int lbtf_op_get_survey(struct iee
+
+ static const struct ieee80211_ops lbtf_ops = {
+ .tx = lbtf_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = lbtf_op_start,
+ .stop = lbtf_op_stop,
+ .add_interface = lbtf_op_add_interface,
+--- a/drivers/net/wireless/marvell/mwl8k.c
++++ b/drivers/net/wireless/marvell/mwl8k.c
+@@ -5611,6 +5611,7 @@ static void mwl8k_sw_scan_complete(struc
+
+ static const struct ieee80211_ops mwl8k_ops = {
+ .tx = mwl8k_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = mwl8k_start,
+ .stop = mwl8k_stop,
+ .add_interface = mwl8k_add_interface,
+--- a/drivers/net/wireless/mediatek/mt7601u/main.c
++++ b/drivers/net/wireless/mediatek/mt7601u/main.c
+@@ -406,6 +406,7 @@ out:
+
+ const struct ieee80211_ops mt7601u_ops = {
+ .tx = mt7601u_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = mt7601u_start,
+ .stop = mt7601u_stop,
+ .add_interface = mt7601u_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
+@@ -1706,6 +1706,7 @@ static int rt2400pci_tx_last_beacon(stru
+
+ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
+@@ -2004,6 +2004,7 @@ static int rt2500pci_tx_last_beacon(stru
+
+ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
+@@ -1795,6 +1795,7 @@ static int rt2500usb_probe_hw(struct rt2
+
+ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+@@ -288,6 +288,7 @@ static int rt2800pci_read_eeprom(struct
+
+ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+@@ -133,6 +133,7 @@ static int rt2800soc_write_firmware(stru
+
+ static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+@@ -630,6 +630,7 @@ static int rt2800usb_probe_hw(struct rt2
+
+ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
+@@ -2873,6 +2873,7 @@ static u64 rt61pci_get_tsf(struct ieee80
+
+ static const struct ieee80211_ops rt61pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
+@@ -2292,6 +2292,7 @@ static u64 rt73usb_get_tsf(struct ieee80
+
+ static const struct ieee80211_ops rt73usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
++++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
+@@ -1608,6 +1608,7 @@ static void rtl8180_configure_filter(str
+
+ static const struct ieee80211_ops rtl8180_ops = {
+ .tx = rtl8180_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rtl8180_start,
+ .stop = rtl8180_stop,
+ .add_interface = rtl8180_add_interface,
+--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
++++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+@@ -1378,6 +1378,7 @@ static int rtl8187_conf_tx(struct ieee80
+
+ static const struct ieee80211_ops rtl8187_ops = {
+ .tx = rtl8187_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rtl8187_start,
+ .stop = rtl8187_stop,
+ .add_interface = rtl8187_add_interface,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+@@ -6561,6 +6561,7 @@ static void rtl8xxxu_stop(struct ieee802
+
+ static const struct ieee80211_ops rtl8xxxu_ops = {
+ .tx = rtl8xxxu_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = rtl8xxxu_add_interface,
+ .remove_interface = rtl8xxxu_remove_interface,
+ .config = rtl8xxxu_config,
+--- a/drivers/net/wireless/realtek/rtlwifi/core.c
++++ b/drivers/net/wireless/realtek/rtlwifi/core.c
+@@ -1912,6 +1912,7 @@ const struct ieee80211_ops rtl_ops = {
+ .start = rtl_op_start,
+ .stop = rtl_op_stop,
+ .tx = rtl_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = rtl_op_add_interface,
+ .remove_interface = rtl_op_remove_interface,
+ .change_interface = rtl_op_change_interface,
+--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
+@@ -896,6 +896,7 @@ static void rtw_ops_sta_rc_update(struct
+
+ const struct ieee80211_ops rtw_ops = {
+ .tx = rtw_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw_ops_wake_tx_queue,
+ .start = rtw_ops_start,
+ .stop = rtw_ops_stop,
+--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
+@@ -918,6 +918,7 @@ static int rtw89_ops_set_tid_config(stru
+
+ const struct ieee80211_ops rtw89_ops = {
+ .tx = rtw89_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw89_ops_wake_tx_queue,
+ .start = rtw89_ops_start,
+ .stop = rtw89_ops_stop,
+--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
++++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+@@ -1958,6 +1958,7 @@ static int rsi_mac80211_resume(struct ie
+
+ static const struct ieee80211_ops mac80211_ops = {
+ .tx = rsi_mac80211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rsi_mac80211_start,
+ .stop = rsi_mac80211_stop,
+ .add_interface = rsi_mac80211_add_interface,
+--- a/drivers/net/wireless/st/cw1200/main.c
++++ b/drivers/net/wireless/st/cw1200/main.c
+@@ -209,6 +209,7 @@ static const struct ieee80211_ops cw1200
+ .remove_interface = cw1200_remove_interface,
+ .change_interface = cw1200_change_interface,
+ .tx = cw1200_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .hw_scan = cw1200_hw_scan,
+ .set_tim = cw1200_set_tim,
+ .sta_notify = cw1200_sta_notify,
+--- a/drivers/net/wireless/ti/wl1251/main.c
++++ b/drivers/net/wireless/ti/wl1251/main.c
+@@ -1359,6 +1359,7 @@ static const struct ieee80211_ops wl1251
+ .prepare_multicast = wl1251_op_prepare_multicast,
+ .configure_filter = wl1251_op_configure_filter,
+ .tx = wl1251_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wl1251_op_set_key,
+ .hw_scan = wl1251_op_hw_scan,
+ .bss_info_changed = wl1251_op_bss_info_changed,
+--- a/drivers/net/wireless/ti/wlcore/main.c
++++ b/drivers/net/wireless/ti/wlcore/main.c
+@@ -5942,6 +5942,7 @@ static const struct ieee80211_ops wl1271
+ .prepare_multicast = wl1271_op_prepare_multicast,
+ .configure_filter = wl1271_op_configure_filter,
+ .tx = wl1271_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wlcore_op_set_key,
+ .hw_scan = wl1271_op_hw_scan,
+ .cancel_hw_scan = wl1271_op_cancel_hw_scan,
+--- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
++++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
+@@ -1344,6 +1344,7 @@ static u64 zd_op_get_tsf(struct ieee8021
+
+ static const struct ieee80211_ops zd_ops = {
+ .tx = zd_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = zd_op_start,
+ .stop = zd_op_stop,
+ .add_interface = zd_op_add_interface,
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:40 +0200
+Subject: [PATCH] wifi: mac80211: Drop support for TX push path
+
+All drivers are now using mac80211 internal queues (iTXQs).
+Drop mac80211 internal support for the old push path.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4339,9 +4339,6 @@ static int ieee80211_get_txq_stats(struc
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0;
+
+- if (!local->ops->wake_tx_queue)
+- return 1;
+-
+ spin_lock_bh(&local->fq.lock);
+ rcu_read_lock();
+
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -663,9 +663,7 @@ void debugfs_hw_add(struct ieee80211_loc
+ DEBUGFS_ADD_MODE(force_tx_status, 0600);
+ DEBUGFS_ADD_MODE(aql_enable, 0600);
+ DEBUGFS_ADD(aql_pending);
+-
+- if (local->ops->wake_tx_queue)
+- DEBUGFS_ADD_MODE(aqm, 0600);
++ DEBUGFS_ADD_MODE(aqm, 0600);
+
+ DEBUGFS_ADD_MODE(airtime_flags, 0600);
+
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -677,8 +677,7 @@ static void add_common_files(struct ieee
+ DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
+ DEBUGFS_ADD(hw_queues);
+
+- if (sdata->local->ops->wake_tx_queue &&
+- sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
++ if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+ sdata->vif.type != NL80211_IFTYPE_NAN)
+ DEBUGFS_ADD(aqm);
+ }
+--- a/net/mac80211/debugfs_sta.c
++++ b/net/mac80211/debugfs_sta.c
+@@ -1056,10 +1056,8 @@ void ieee80211_sta_debugfs_add(struct st
+ DEBUGFS_ADD_COUNTER(rx_fragments, deflink.rx_stats.fragments);
+ DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered);
+
+- if (local->ops->wake_tx_queue) {
+- DEBUGFS_ADD(aqm);
+- DEBUGFS_ADD(airtime);
+- }
++ DEBUGFS_ADD(aqm);
++ DEBUGFS_ADD(airtime);
+
+ if (wiphy_ext_feature_isset(local->hw.wiphy,
+ NL80211_EXT_FEATURE_AQL))
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2290,7 +2290,6 @@ void ieee80211_wake_queue_by_reason(stru
+ void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason,
+ bool refcounted);
+-void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
+ void ieee80211_add_pending_skb(struct ieee80211_local *local,
+ struct sk_buff *skb);
+ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -458,12 +458,6 @@ static void ieee80211_do_stop(struct iee
+ if (cancel_scan)
+ ieee80211_scan_cancel(local);
+
+- /*
+- * Stop TX on this interface first.
+- */
+- if (!local->ops->wake_tx_queue && sdata->dev)
+- netif_tx_stop_all_queues(sdata->dev);
+-
+ ieee80211_roc_purge(local, sdata);
+
+ switch (sdata->vif.type) {
+@@ -811,13 +805,6 @@ static void ieee80211_uninit(struct net_
+ ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
+ }
+
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev)
+-{
+- return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
+-}
+-
+ static void
+ ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+ {
+@@ -831,7 +818,6 @@ static const struct net_device_ops ieee8
+ .ndo_start_xmit = ieee80211_subif_start_xmit,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+ };
+
+@@ -939,7 +925,6 @@ static const struct net_device_ops ieee8
+ .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
+ };
+@@ -1441,35 +1426,6 @@ int ieee80211_do_open(struct wireless_de
+
+ ieee80211_recalc_ps(local);
+
+- if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+- sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+- local->ops->wake_tx_queue) {
+- /* XXX: for AP_VLAN, actually track AP queues */
+- if (dev)
+- netif_tx_start_all_queues(dev);
+- } else if (dev) {
+- unsigned long flags;
+- int n_acs = IEEE80211_NUM_ACS;
+- int ac;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS)
+- n_acs = 1;
+-
+- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+- if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE ||
+- (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 &&
+- skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) {
+- for (ac = 0; ac < n_acs; ac++) {
+- int ac_queue = sdata->vif.hw_queue[ac];
+-
+- if (local->queue_stop_reasons[ac_queue] == 0 &&
+- skb_queue_empty(&local->pending[ac_queue]))
+- netif_start_subqueue(dev, ac);
+- }
+- }
+- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+- }
+-
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ return 0;
+@@ -1499,17 +1455,12 @@ static void ieee80211_if_setup(struct ne
+ {
+ ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
++ dev->priv_flags |= IFF_NO_QUEUE;
+ dev->netdev_ops = &ieee80211_dataif_ops;
+ dev->needs_free_netdev = true;
+ dev->priv_destructor = ieee80211_if_free;
+ }
+
+-static void ieee80211_if_setup_no_queue(struct net_device *dev)
+-{
+- ieee80211_if_setup(dev);
+- dev->priv_flags |= IFF_NO_QUEUE;
+-}
+-
+ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+@@ -2094,9 +2045,7 @@ int ieee80211_if_add(struct ieee80211_lo
+ struct net_device *ndev = NULL;
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct txq_info *txqi;
+- void (*if_setup)(struct net_device *dev);
+ int ret, i;
+- int txqs = 1;
+
+ ASSERT_RTNL();
+
+@@ -2119,30 +2068,18 @@ int ieee80211_if_add(struct ieee80211_lo
+ sizeof(void *));
+ int txq_size = 0;
+
+- if (local->ops->wake_tx_queue &&
+- type != NL80211_IFTYPE_AP_VLAN &&
++ if (type != NL80211_IFTYPE_AP_VLAN &&
+ (type != NL80211_IFTYPE_MONITOR ||
+ (params->flags & MONITOR_FLAG_ACTIVE)))
+ txq_size += sizeof(struct txq_info) +
+ local->hw.txq_data_size;
+
+- if (local->ops->wake_tx_queue) {
+- if_setup = ieee80211_if_setup_no_queue;
+- } else {
+- if_setup = ieee80211_if_setup;
+- if (local->hw.queues >= IEEE80211_NUM_ACS)
+- txqs = IEEE80211_NUM_ACS;
+- }
+-
+ ndev = alloc_netdev_mqs(size + txq_size,
+ name, name_assign_type,
+- if_setup, txqs, 1);
++ ieee80211_if_setup, 1, 1);
+ if (!ndev)
+ return -ENOMEM;
+
+- if (!local->ops->wake_tx_queue && local->hw.wiphy->tx_queue_len)
+- ndev->tx_queue_len = local->hw.wiphy->tx_queue_len;
+-
+ dev_net_set(ndev, wiphy_net(local->hw.wiphy));
+
+ ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -630,7 +630,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+
+ if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
+ !ops->add_interface || !ops->remove_interface ||
+- !ops->configure_filter))
++ !ops->configure_filter || !ops->wake_tx_queue))
+ return NULL;
+
+ if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
+@@ -719,9 +719,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ if (!ops->set_key)
+ wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
+- if (ops->wake_tx_queue)
+- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
+-
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
+
+ wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
+@@ -834,10 +832,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ atomic_set(&local->agg_queue_stop[i], 0);
+ }
+ tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending);
+-
+- if (ops->wake_tx_queue)
+- tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+-
++ tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+ tasklet_setup(&local->tasklet, ieee80211_tasklet_handler);
+
+ skb_queue_head_init(&local->skb_queue);
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1571,9 +1571,6 @@ static void sta_ps_start(struct sta_info
+
+ ieee80211_clear_fast_xmit(sta);
+
+- if (!sta->sta.txq[0])
+- return;
+-
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi = to_txq_info(txq);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -140,17 +140,15 @@ static void __cleanup_single_sta(struct
+ atomic_dec(&ps->num_sta_ps);
+ }
+
+- if (sta->sta.txq[0]) {
+- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+- struct txq_info *txqi;
++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
++ struct txq_info *txqi;
+
+- if (!sta->sta.txq[i])
+- continue;
++ if (!sta->sta.txq[i])
++ continue;
+
+- txqi = to_txq_info(sta->sta.txq[i]);
++ txqi = to_txq_info(sta->sta.txq[i]);
+
+- ieee80211_txq_purge(local, txqi);
+- }
++ ieee80211_txq_purge(local, txqi);
+ }
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+@@ -425,8 +423,7 @@ void sta_info_free(struct ieee80211_loca
+
+ sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
+
+- if (sta->sta.txq[0])
+- kfree(to_txq_info(sta->sta.txq[0]));
++ kfree(to_txq_info(sta->sta.txq[0]));
+ kfree(rcu_dereference_raw(sta->sta.rates));
+ #ifdef CPTCFG_MAC80211_MESH
+ kfree(sta->mesh);
+@@ -527,6 +524,8 @@ __sta_info_alloc(struct ieee80211_sub_if
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
++ void *txq_data;
++ int size;
+ int i;
+
+ sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
+@@ -597,21 +596,18 @@ __sta_info_alloc(struct ieee80211_sub_if
+
+ sta->last_connected = ktime_get_seconds();
+
+- if (local->ops->wake_tx_queue) {
+- void *txq_data;
+- int size = sizeof(struct txq_info) +
+- ALIGN(hw->txq_data_size, sizeof(void *));
++ size = sizeof(struct txq_info) +
++ ALIGN(hw->txq_data_size, sizeof(void *));
+
+- txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+- if (!txq_data)
+- goto free;
++ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
++ if (!txq_data)
++ goto free;
+
+- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+- struct txq_info *txq = txq_data + i * size;
++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
++ struct txq_info *txq = txq_data + i * size;
+
+- /* might not do anything for the bufferable MMPDU TXQ */
+- ieee80211_txq_init(sdata, sta, txq, i);
+- }
++ /* might not do anything for the (bufferable) MMPDU TXQ */
++ ieee80211_txq_init(sdata, sta, txq, i);
+ }
+
+ if (sta_prepare_rate_control(local, sta, gfp))
+@@ -685,8 +681,7 @@ __sta_info_alloc(struct ieee80211_sub_if
+ return sta;
+
+ free_txq:
+- if (sta->sta.txq[0])
+- kfree(to_txq_info(sta->sta.txq[0]));
++ kfree(to_txq_info(sta->sta.txq[0]));
+ free:
+ sta_info_free_link(&sta->deflink);
+ #ifdef CPTCFG_MAC80211_MESH
+@@ -1959,9 +1954,6 @@ ieee80211_sta_ps_deliver_response(struct
+ * TIM recalculation.
+ */
+
+- if (!sta->sta.txq[0])
+- return;
+-
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ if (!sta->sta.txq[tid] ||
+ !(driver_release_tids & BIT(tid)) ||
+@@ -2446,7 +2438,7 @@ static void sta_set_tidstats(struct sta_
+ tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid];
+ }
+
+- if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) {
++ if (tid < IEEE80211_NUM_TIDS) {
+ spin_lock_bh(&local->fq.lock);
+ rcu_read_lock();
+
+@@ -2774,9 +2766,6 @@ unsigned long ieee80211_sta_last_active(
+
+ static void sta_update_codel_params(struct sta_info *sta, u32 thr)
+ {
+- if (!sta->sdata->local->ops->wake_tx_queue)
+- return;
+-
+ if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
+ sta->cparams.target = MS2TIME(50);
+ sta->cparams.interval = MS2TIME(300);
+--- a/net/mac80211/tdls.c
++++ b/net/mac80211/tdls.c
+@@ -1016,7 +1016,6 @@ ieee80211_tdls_prep_mgmt_packet(struct w
+ skb->priority = 256 + 5;
+ break;
+ }
+- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));
+
+ /*
+ * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1599,9 +1599,6 @@ int ieee80211_txq_setup_flows(struct iee
+ bool supp_vht = false;
+ enum nl80211_band band;
+
+- if (!local->ops->wake_tx_queue)
+- return 0;
+-
+ ret = fq_init(fq, 4096);
+ if (ret)
+ return ret;
+@@ -1649,9 +1646,6 @@ void ieee80211_txq_teardown_flows(struct
+ {
+ struct fq *fq = &local->fq;
+
+- if (!local->ops->wake_tx_queue)
+- return;
+-
+ kfree(local->cvars);
+ local->cvars = NULL;
+
+@@ -1668,8 +1662,7 @@ static bool ieee80211_queue_skb(struct i
+ struct ieee80211_vif *vif;
+ struct txq_info *txqi;
+
+- if (!local->ops->wake_tx_queue ||
+- sdata->vif.type == NL80211_IFTYPE_MONITOR)
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ return false;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+@@ -4185,12 +4178,7 @@ void __ieee80211_subif_start_xmit(struct
+ if (IS_ERR(sta))
+ sta = NULL;
+
+- if (local->ops->wake_tx_queue) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+- skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+- }
+-
++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+ ieee80211_aggr_check(sdata, sta, skb);
+
+ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
+@@ -4501,11 +4489,7 @@ static void ieee80211_8023_xmit(struct i
+ struct tid_ampdu_tx *tid_tx;
+ u8 tid;
+
+- if (local->ops->wake_tx_queue) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+- skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+- }
++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+@@ -4759,9 +4743,6 @@ void ieee80211_tx_pending(struct tasklet
+ if (!txok)
+ break;
+ }
+-
+- if (skb_queue_empty(&local->pending[i]))
+- ieee80211_propagate_queue_wake(local, i);
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+@@ -5954,10 +5935,9 @@ int ieee80211_tx_control_port(struct wip
+ }
+
+ if (!IS_ERR(sta)) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
++ u16 queue = ieee80211_select_queue(sdata, sta, skb);
+
+ skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+
+ /*
+ * for MLO STA, the SA should be the AP MLD address, but
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -446,39 +446,6 @@ void ieee80211_wake_txqs(struct tasklet_
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ }
+
+-void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
+-{
+- struct ieee80211_sub_if_data *sdata;
+- int n_acs = IEEE80211_NUM_ACS;
+-
+- if (local->ops->wake_tx_queue)
+- return;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS)
+- n_acs = 1;
+-
+- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+- int ac;
+-
+- if (!sdata->dev)
+- continue;
+-
+- if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
+- local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
+- continue;
+-
+- for (ac = 0; ac < n_acs; ac++) {
+- int ac_queue = sdata->vif.hw_queue[ac];
+-
+- if (ac_queue == queue ||
+- (sdata->vif.cab_queue == queue &&
+- local->queue_stop_reasons[ac_queue] == 0 &&
+- skb_queue_empty(&local->pending[ac_queue])))
+- netif_wake_subqueue(sdata->dev, ac);
+- }
+- }
+-}
+-
+ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason,
+ bool refcounted,
+@@ -509,11 +476,7 @@ static void __ieee80211_wake_queue(struc
+ /* someone still has this queue stopped */
+ return;
+
+- if (skb_queue_empty(&local->pending[queue])) {
+- rcu_read_lock();
+- ieee80211_propagate_queue_wake(local, queue);
+- rcu_read_unlock();
+- } else
++ if (!skb_queue_empty(&local->pending[queue]))
+ tasklet_schedule(&local->tx_pending_tasklet);
+
+ /*
+@@ -523,12 +486,10 @@ static void __ieee80211_wake_queue(struc
+ * release someone's lock, but it is fine because all the callers of
+ * __ieee80211_wake_queue call it right before releasing the lock.
+ */
+- if (local->ops->wake_tx_queue) {
+- if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
+- tasklet_schedule(&local->wake_txqs_tasklet);
+- else
+- _ieee80211_wake_txqs(local, flags);
+- }
++ if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
++ tasklet_schedule(&local->wake_txqs_tasklet);
++ else
++ _ieee80211_wake_txqs(local, flags);
+ }
+
+ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+@@ -585,10 +546,6 @@ static void __ieee80211_stop_queue(struc
+ for (ac = 0; ac < n_acs; ac++) {
+ if (sdata->vif.hw_queue[ac] == queue ||
+ sdata->vif.cab_queue == queue) {
+- if (!local->ops->wake_tx_queue) {
+- netif_stop_subqueue(sdata->dev, ac);
+- continue;
+- }
+ spin_lock(&local->fq.lock);
+ sdata->vif.txqs_stopped[ac] = true;
+ spin_unlock(&local->fq.lock);
+--- a/net/mac80211/wme.c
++++ b/net/mac80211/wme.c
+@@ -122,6 +122,9 @@ u16 ieee80211_select_queue_80211(struct
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 *p;
+
++ /* Ensure hash is set prior to potential SW encryption */
++ skb_get_hash(skb);
++
+ if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) ||
+ local->hw.queues < IEEE80211_NUM_ACS)
+ return 0;
+@@ -141,12 +144,15 @@ u16 ieee80211_select_queue_80211(struct
+ return ieee80211_downgrade_queue(sdata, NULL, skb);
+ }
+
+-u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta, struct sk_buff *skb)
++u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta, struct sk_buff *skb)
+ {
+ struct mac80211_qos_map *qos_map;
+ bool qos;
+
++ /* Ensure hash is set prior to potential SW encryption */
++ skb_get_hash(skb);
++
+ /* all mesh/ocb stations are required to support WME */
+ if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
+ sdata->vif.type == NL80211_IFTYPE_OCB))
+@@ -176,59 +182,6 @@ u16 __ieee80211_select_queue(struct ieee
+ return ieee80211_downgrade_queue(sdata, sta, skb);
+ }
+
+-
+-/* Indicate which queue to use. */
+-u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb)
+-{
+- struct ieee80211_local *local = sdata->local;
+- struct sta_info *sta = NULL;
+- const u8 *ra = NULL;
+- u16 ret;
+-
+- /* when using iTXQ, we can do this later */
+- if (local->ops->wake_tx_queue)
+- return 0;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
+- skb->priority = 0; /* required for correct WPA/11i MIC */
+- return 0;
+- }
+-
+- rcu_read_lock();
+- switch (sdata->vif.type) {
+- case NL80211_IFTYPE_AP_VLAN:
+- sta = rcu_dereference(sdata->u.vlan.sta);
+- if (sta)
+- break;
+- fallthrough;
+- case NL80211_IFTYPE_AP:
+- ra = skb->data;
+- break;
+- case NL80211_IFTYPE_STATION:
+- /* might be a TDLS station */
+- sta = sta_info_get(sdata, skb->data);
+- if (sta)
+- break;
+-
+- ra = sdata->deflink.u.mgd.bssid;
+- break;
+- case NL80211_IFTYPE_ADHOC:
+- ra = skb->data;
+- break;
+- default:
+- break;
+- }
+-
+- if (!sta && ra && !is_multicast_ether_addr(ra))
+- sta = sta_info_get(sdata, ra);
+-
+- ret = __ieee80211_select_queue(sdata, sta, skb);
+-
+- rcu_read_unlock();
+- return ret;
+-}
+-
+ /**
+ * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
+ *
+--- a/net/mac80211/wme.h
++++ b/net/mac80211/wme.h
+@@ -13,10 +13,8 @@
+ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct ieee80211_hdr *hdr);
+-u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta, struct sk_buff *skb);
+ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb);
++ struct sta_info *sta, struct sk_buff *skb);
+ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
+
--- /dev/null
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Mon, 10 Oct 2022 19:17:46 +0200
+Subject: [PATCH] wifi: realtek: remove duplicated wake_tx_queue
+
+By accident, the previous patch duplicated the initialization
+of the wake_tx_queue callback. Fix that by removing the new
+initializations.
+
+Fixes: a790cc3a4fad ("wifi: mac80211: add wake_tx_queue callback to drivers")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
+@@ -896,7 +896,6 @@ static void rtw_ops_sta_rc_update(struct
+
+ const struct ieee80211_ops rtw_ops = {
+ .tx = rtw_ops_tx,
+- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw_ops_wake_tx_queue,
+ .start = rtw_ops_start,
+ .stop = rtw_ops_stop,
+--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
+@@ -918,7 +918,6 @@ static int rtw89_ops_set_tid_config(stru
+
+ const struct ieee80211_ops rtw89_ops = {
+ .tx = rtw89_ops_tx,
+- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw89_ops_wake_tx_queue,
+ .start = rtw89_ops_start,
+ .stop = rtw89_ops_stop,
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 14 Dec 2021 17:53:12 +0100
-Subject: [PATCH] mac80211: use coarse boottime for airtime fairness code
-
-The time values used by the airtime fairness code only need to be accurate
-enough to cover station activity detection.
-Using ktime_get_coarse_boottime_ns instead of ktime_get_boottime_ns will
-drop the accuracy down to jiffies intervals, but at the same time saves
-a lot of CPU cycles in a hot path
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3820,7 +3820,7 @@ struct ieee80211_txq *ieee80211_next_txq
- {
- struct ieee80211_local *local = hw_to_local(hw);
- struct airtime_sched_info *air_sched;
-- u64 now = ktime_get_boottime_ns();
-+ u64 now = ktime_get_coarse_boottime_ns();
- struct ieee80211_txq *ret = NULL;
- struct airtime_info *air_info;
- struct txq_info *txqi = NULL;
-@@ -3947,7 +3947,7 @@ void ieee80211_update_airtime_weight(str
- u64 weight_sum = 0;
-
- if (unlikely(!now))
-- now = ktime_get_boottime_ns();
-+ now = ktime_get_coarse_boottime_ns();
-
- lockdep_assert_held(&air_sched->lock);
-
-@@ -3973,7 +3973,7 @@ void ieee80211_schedule_txq(struct ieee8
- struct ieee80211_local *local = hw_to_local(hw);
- struct txq_info *txqi = to_txq_info(txq);
- struct airtime_sched_info *air_sched;
-- u64 now = ktime_get_boottime_ns();
-+ u64 now = ktime_get_coarse_boottime_ns();
- struct airtime_info *air_info;
- u8 ac = txq->ac;
- bool was_active;
-@@ -4031,7 +4031,7 @@ static void __ieee80211_unschedule_txq(s
-
- if (!purge)
- airtime_set_active(air_sched, air_info,
-- ktime_get_boottime_ns());
-+ ktime_get_coarse_boottime_ns());
-
- rb_erase_cached(&txqi->schedule_order,
- &air_sched->active_txqs);
-@@ -4119,7 +4119,7 @@ bool ieee80211_txq_may_transmit(struct i
- if (RB_EMPTY_NODE(&txqi->schedule_order))
- goto out;
-
-- now = ktime_get_boottime_ns();
-+ now = ktime_get_coarse_boottime_ns();
-
- /* Like in ieee80211_next_txq(), make sure the first station in the
- * scheduling order is eligible for transmission to avoid starvation.
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 24 May 2021 11:46:09 +0200
-Subject: [PATCH] mac80211_hwsim: make 6 GHz channels usable
-
-The previous commit that claimed to add 6 GHz channels didn't actually make
-them usable, since the 6 GHz band was not registered with mac80211.
-
-Fixes: 28881922abd7 ("mac80211_hwsim: add 6GHz channels")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/mac80211_hwsim.c
-+++ b/drivers/net/wireless/mac80211_hwsim.c
-@@ -3008,15 +3008,19 @@ static void mac80211_hwsim_he_capab(stru
- {
- u16 n_iftype_data;
-
-- if (sband->band == NL80211_BAND_2GHZ) {
-+ switch (sband->band) {
-+ case NL80211_BAND_2GHZ:
- n_iftype_data = ARRAY_SIZE(he_capa_2ghz);
- sband->iftype_data =
- (struct ieee80211_sband_iftype_data *)he_capa_2ghz;
-- } else if (sband->band == NL80211_BAND_5GHZ) {
-+ break;
-+ case NL80211_BAND_5GHZ:
-+ case NL80211_BAND_6GHZ:
- n_iftype_data = ARRAY_SIZE(he_capa_5ghz);
- sband->iftype_data =
- (struct ieee80211_sband_iftype_data *)he_capa_5ghz;
-- } else {
-+ break;
-+ default:
- return;
- }
-
-@@ -3306,6 +3310,12 @@ static int mac80211_hwsim_new_radio(stru
- sband->vht_cap.vht_mcs.tx_mcs_map =
- sband->vht_cap.vht_mcs.rx_mcs_map;
- break;
-+ case NL80211_BAND_6GHZ:
-+ sband->channels = data->channels_6ghz;
-+ sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz);
-+ sband->bitrates = data->rates + 4;
-+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
-+ break;
- case NL80211_BAND_S1GHZ:
- memcpy(&sband->s1g_cap, &hwsim_s1g_cap,
- sizeof(sband->s1g_cap));
-@@ -3316,6 +3326,13 @@ static int mac80211_hwsim_new_radio(stru
- continue;
- }
-
-+ mac80211_hwsim_he_capab(sband);
-+
-+ hw->wiphy->bands[band] = sband;
-+
-+ if (band == NL80211_BAND_6GHZ)
-+ continue;
-+
- sband->ht_cap.ht_supported = true;
- sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_GRN_FLD |
-@@ -3329,10 +3346,6 @@ static int mac80211_hwsim_new_radio(stru
- sband->ht_cap.mcs.rx_mask[0] = 0xff;
- sband->ht_cap.mcs.rx_mask[1] = 0xff;
- sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
--
-- mac80211_hwsim_he_capab(sband);
--
-- hw->wiphy->bands[band] = sband;
- }
-
- /* By default all radios belong to the first group */
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 12 Nov 2021 12:22:23 +0100
-Subject: [PATCH] mac80211: add support for .ndo_fill_forward_path
-
-This allows drivers to provide a destination device + info for flow offload
-Only supported in combination with 802.3 encap offload
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Tested-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/20211112112223.1209-1-nbd@nbd.name
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3937,6 +3937,8 @@ struct ieee80211_prep_tx_info {
- * twt structure.
- * @twt_teardown_request: Update the hw with TWT teardown request received
- * from the peer.
-+ * @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
-+ * resolve a path for hardware flow offloading
- */
- struct ieee80211_ops {
- void (*tx)(struct ieee80211_hw *hw,
-@@ -4265,6 +4267,13 @@ struct ieee80211_ops {
- struct ieee80211_twt_setup *twt);
- void (*twt_teardown_request)(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u8 flowid);
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
-+ int (*net_fill_forward_path)(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif,
-+ struct ieee80211_sta *sta,
-+ struct net_device_path_ctx *ctx,
-+ struct net_device_path *path);
-+#endif
- };
-
- /**
---- a/net/mac80211/driver-ops.h
-+++ b/net/mac80211/driver-ops.h
-@@ -1486,4 +1486,28 @@ static inline void drv_twt_teardown_requ
- trace_drv_return_void(local);
- }
-
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
-+static inline int drv_net_fill_forward_path(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_sta *sta,
-+ struct net_device_path_ctx *ctx,
-+ struct net_device_path *path)
-+{
-+ int ret = -EOPNOTSUPP;
-+
-+ sdata = get_bss_sdata(sdata);
-+ if (!check_sdata_in_driver(sdata))
-+ return -EIO;
-+
-+ trace_drv_net_fill_forward_path(local, sdata, sta);
-+ if (local->ops->net_fill_forward_path)
-+ ret = local->ops->net_fill_forward_path(&local->hw,
-+ &sdata->vif, sta,
-+ ctx, path);
-+ trace_drv_return_int(local, ret);
-+
-+ return ret;
-+}
-+#endif
-+
- #endif /* __MAC80211_DRIVER_OPS */
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1489,7 +1489,7 @@ struct ieee80211_local {
- };
-
- static inline struct ieee80211_sub_if_data *
--IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)
-+IEEE80211_DEV_TO_SUB_IF(const struct net_device *dev)
- {
- return netdev_priv(dev);
- }
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -822,6 +822,66 @@ static const struct net_device_ops ieee8
-
- };
-
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
-+static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
-+ struct net_device_path *path)
-+{
-+ struct ieee80211_sub_if_data *sdata;
-+ struct ieee80211_local *local;
-+ struct sta_info *sta;
-+ int ret = -ENOENT;
-+
-+ sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
-+ local = sdata->local;
-+
-+ if (!local->ops->net_fill_forward_path)
-+ return -EOPNOTSUPP;
-+
-+ rcu_read_lock();
-+ switch (sdata->vif.type) {
-+ case NL80211_IFTYPE_AP_VLAN:
-+ sta = rcu_dereference(sdata->u.vlan.sta);
-+ if (sta)
-+ break;
-+ if (sdata->wdev.use_4addr)
-+ goto out;
-+ if (is_multicast_ether_addr(ctx->daddr))
-+ goto out;
-+ sta = sta_info_get_bss(sdata, ctx->daddr);
-+ break;
-+ case NL80211_IFTYPE_AP:
-+ if (is_multicast_ether_addr(ctx->daddr))
-+ goto out;
-+ sta = sta_info_get(sdata, ctx->daddr);
-+ break;
-+ case NL80211_IFTYPE_STATION:
-+ if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
-+ sta = sta_info_get(sdata, ctx->daddr);
-+ if (sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
-+ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
-+ goto out;
-+
-+ break;
-+ }
-+ }
-+
-+ sta = sta_info_get(sdata, sdata->u.mgd.bssid);
-+ break;
-+ default:
-+ goto out;
-+ }
-+
-+ if (!sta)
-+ goto out;
-+
-+ ret = drv_net_fill_forward_path(local, sdata, &sta->sta, ctx, path);
-+out:
-+ rcu_read_unlock();
-+
-+ return ret;
-+}
-+#endif
-+
- static const struct net_device_ops ieee80211_dataif_8023_ops = {
- #if LINUX_VERSION_IS_LESS(4,10,0)
- .ndo_change_mtu = __change_mtu,
-@@ -839,7 +899,9 @@ static const struct net_device_ops ieee8
- #else
- .ndo_get_stats64 = bp_ieee80211_get_stats64,
- #endif
--
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
-+ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
-+#endif
- };
-
- static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype)
---- a/net/mac80211/trace.h
-+++ b/net/mac80211/trace.h
-@@ -2892,6 +2892,15 @@ TRACE_EVENT(drv_twt_teardown_request,
- )
- );
-
-+#if LINUX_VERSION_IS_GEQ(5,10,0)
-+DEFINE_EVENT(sta_event, drv_net_fill_forward_path,
-+ TP_PROTO(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_sta *sta),
-+ TP_ARGS(local, sdata, sta)
-+);
-+#endif
-+
- #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Apr 2021 21:03:13 +0200
-Subject: [PATCH] mac80211: minstrel_ht: fix MINSTREL_FRAC macro
-
-Add missing braces to avoid issues with e.g. using additions in the
-div expression
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.h
-+++ b/net/mac80211/rc80211_minstrel_ht.h
-@@ -14,7 +14,7 @@
-
- /* scaled fraction values */
- #define MINSTREL_SCALE 12
--#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
- #define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
-
- #define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 9 Oct 2022 20:15:46 +0200
+Subject: [PATCH] mac80211: add support for restricting netdev features per vif
+
+This can be used to selectively disable feature flags for checksum offload,
+scatter/gather or GSO by changing vif->netdev_features.
+Removing features from vif->netdev_features does not affect the netdev
+features themselves, but instead fixes up skbs in the tx path so that the
+offloads are not needed in the driver.
+
+Aside from making it easier to deal with vif type based hardware limitations,
+this also makes it possible to optimize performance on hardware without native
+GSO support by declaring GSO support in hw->netdev_features and removing it
+from vif->netdev_features. This allows mac80211 to handle GSO segmentation
+after the sta lookup, but before itxq enqueue, thus reducing the number of
+unnecessary sta lookups, as well as some other per-packet processing.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/fq_impl.h
++++ b/include/net/fq_impl.h
+@@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq
+ fq_skb_free_t free_func)
+ {
+ struct fq_flow *flow;
++ struct sk_buff *next;
+ bool oom;
+
+ lockdep_assert_held(&fq->lock);
+@@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq
+ }
+
+ flow->tin = tin;
+- flow->backlog += skb->len;
+- tin->backlog_bytes += skb->len;
+- tin->backlog_packets++;
+- fq->memory_usage += skb->truesize;
+- fq->backlog++;
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ flow->backlog += skb->len;
++ tin->backlog_bytes += skb->len;
++ tin->backlog_packets++;
++ fq->memory_usage += skb->truesize;
++ fq->backlog++;
++ __skb_queue_tail(&flow->queue, skb);
++ }
+
+ if (list_empty(&flow->flowchain)) {
+ flow->deficit = fq->quantum;
+@@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq
+ &tin->new_flows);
+ }
+
+- __skb_queue_tail(&flow->queue, skb);
+ oom = (fq->memory_usage > fq->memory_limit);
+ while (fq->backlog > fq->limit || oom) {
+ flow = fq_find_fattest_flow(fq);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg {
+ * @addr: address of this interface
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ * interface, i.e. a GO or p2p-sta respectively
++ * @netdev_features: tx netdev features supported by the hardware for this
++ * vif. mac80211 initializes this to hw->netdev_features, and the driver
++ * can mask out specific tx features. mac80211 will handle software fixup
++ * for masked offloads (GSO, CSUM)
+ * @driver_flags: flags/capabilities the driver has for this interface,
+ * these need to be set (or cleared) when the interface is added
+ * or, if supported by the driver, the interface type is changed
+@@ -1848,6 +1852,7 @@ struct ieee80211_vif {
+
+ struct ieee80211_txq *txq;
+
++ netdev_features_t netdev_features;
+ u32 driver_flags;
+ u32 offload_flags;
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -2179,6 +2179,7 @@ int ieee80211_if_add(struct ieee80211_lo
+ ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ ndev->hw_features |= ndev->features &
+ MAC80211_SUPPORTED_FEATURES_TX;
++ sdata->vif.netdev_features = local->hw.netdev_features;
+
+ netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_tx
+
+ static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
+ {
+- IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
++ struct sk_buff *next;
++ codel_time_t now = codel_get_time();
++
++ skb_list_walk_safe(skb, skb, next)
++ IEEE80211_SKB_CB(skb)->control.enqueue_time = now;
+ }
+
+ static u32 codel_skb_len_func(const struct sk_buff *skb)
+@@ -3578,55 +3582,79 @@ ieee80211_xmit_fast_finish(struct ieee80
+ return TX_CONTINUE;
+ }
+
+-static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta,
+- struct ieee80211_fast_tx *fast_tx,
+- struct sk_buff *skb)
++static netdev_features_t
++ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata)
+ {
+- struct ieee80211_local *local = sdata->local;
+- u16 ethertype = (skb->data[12] << 8) | skb->data[13];
+- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
+- int hw_headroom = sdata->local->hw.extra_tx_headroom;
+- struct ethhdr eth;
+- struct ieee80211_tx_info *info;
+- struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+- struct ieee80211_tx_data tx;
+- ieee80211_tx_result r;
+- struct tid_ampdu_tx *tid_tx = NULL;
+- u8 tid = IEEE80211_NUM_TIDS;
++ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
++ return sdata->vif.netdev_features;
+
+- /* control port protocol needs a lot of special handling */
+- if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
+- return false;
++ if (!sdata->bss)
++ return 0;
+
+- /* only RFC 1042 SNAP */
+- if (ethertype < ETH_P_802_3_MIN)
+- return false;
++ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++ return sdata->vif.netdev_features;
++}
+
+- /* don't handle TX status request here either */
+- if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
+- return false;
++static struct sk_buff *
++ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features)
++{
++ if (skb_is_gso(skb)) {
++ struct sk_buff *segs;
+
+- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+- if (tid_tx) {
+- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
+- return false;
+- if (tid_tx->timeout)
+- tid_tx->last_tx = jiffies;
+- }
++ segs = skb_gso_segment(skb, features);
++ if (!segs)
++ return skb;
++ if (IS_ERR(segs))
++ goto free;
++
++ consume_skb(skb);
++ return segs;
+ }
+
+- /* after this point (skb is modified) we cannot return false */
++ if (skb_needs_linearize(skb, features) && __skb_linearize(skb))
++ goto free;
++
++ if (skb->ip_summed == CHECKSUM_PARTIAL) {
++ int ofs = skb_checksum_start_offset(skb);
++
++ if (skb->encapsulation)
++ skb_set_inner_transport_header(skb, ofs);
++ else
++ skb_set_transport_header(skb, ofs);
++
++ if (skb_csum_hwoffload_help(skb, features))
++ goto free;
++ }
++
++ skb_mark_not_on_list(skb);
++ return skb;
++
++free:
++ kfree_skb(skb);
++ return NULL;
++}
++
++static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb, u8 tid, bool ampdu)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
++ struct ieee80211_tx_info *info;
++ struct ieee80211_tx_data tx;
++ ieee80211_tx_result r;
++ int hw_headroom = sdata->local->hw.extra_tx_headroom;
++ int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
++ struct ethhdr eth;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (unlikely(!skb))
+- return true;
++ return;
+
+ if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
+ ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
+- return true;
++ return;
+
+ /* will not be crypto-handled beyond what we do here, so use false
+ * as the may-encrypt argument for the resize to not account for
+@@ -3635,10 +3663,8 @@ static bool ieee80211_xmit_fast(struct i
+ if (unlikely(ieee80211_skb_resize(sdata, skb,
+ max_t(int, extra_head + hw_headroom -
+ skb_headroom(skb), 0),
+- ENCRYPT_NO))) {
+- kfree_skb(skb);
+- return true;
+- }
++ ENCRYPT_NO)))
++ goto free;
+
+ memcpy(ð, skb->data, ETH_HLEN - 2);
+ hdr = skb_push(skb, extra_head);
+@@ -3652,7 +3678,7 @@ static bool ieee80211_xmit_fast(struct i
+ info->control.vif = &sdata->vif;
+ info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
+ IEEE80211_TX_CTL_DONTFRAG |
+- (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
++ (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
+ info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT |
+ u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
+ IEEE80211_TX_CTRL_MLO_LINK);
+@@ -3676,16 +3702,14 @@ static bool ieee80211_xmit_fast(struct i
+ tx.key = fast_tx->key;
+
+ if (ieee80211_queue_skb(local, sdata, sta, skb))
+- return true;
++ return;
+
+ tx.skb = skb;
+ r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
+ fast_tx->key, &tx);
+ tx.skb = NULL;
+- if (r == TX_DROP) {
+- kfree_skb(skb);
+- return true;
+- }
++ if (r == TX_DROP)
++ goto free;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+@@ -3693,6 +3717,56 @@ static bool ieee80211_xmit_fast(struct i
+
+ __skb_queue_tail(&tx.skbs, skb);
+ ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
++ return;
++
++free:
++ kfree_skb(skb);
++}
++
++static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb)
++{
++ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
++ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
++ struct tid_ampdu_tx *tid_tx = NULL;
++ struct sk_buff *next;
++ u8 tid = IEEE80211_NUM_TIDS;
++
++ /* control port protocol needs a lot of special handling */
++ if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
++ return false;
++
++ /* only RFC 1042 SNAP */
++ if (ethertype < ETH_P_802_3_MIN)
++ return false;
++
++ /* don't handle TX status request here either */
++ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
++ return false;
++
++ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
++ return false;
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
++ }
++ }
++
++ /* after this point (skb is modified) we cannot return false */
++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
++ if (!skb)
++ return true;
++
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
++ }
++
+ return true;
+ }
+
+@@ -4193,31 +4267,14 @@ void __ieee80211_subif_start_xmit(struct
+ goto out;
+ }
+
+- if (skb_is_gso(skb)) {
+- struct sk_buff *segs;
+-
+- segs = skb_gso_segment(skb, 0);
+- if (IS_ERR(segs)) {
+- goto out_free;
+- } else if (segs) {
+- consume_skb(skb);
+- skb = segs;
+- }
+- } else {
+- /* we cannot process non-linear frames on this path */
+- if (skb_linearize(skb))
+- goto out_free;
+-
+- /* the frame could be fragmented, software-encrypted, and other
+- * things so we cannot really handle checksum offload with it -
+- * fix it up in software before we handle anything else.
+- */
+- if (skb->ip_summed == CHECKSUM_PARTIAL) {
+- skb_set_transport_header(skb,
+- skb_checksum_start_offset(skb));
+- if (skb_checksum_help(skb))
+- goto out_free;
+- }
++ /* the frame could be fragmented, software-encrypted, and other
++ * things so we cannot really handle checksum or GSO offload.
++ * fix it up in software before we handle anything else.
++ */
++ skb = ieee80211_tx_skb_fixup(skb, 0);
++ if (!skb) {
++ len = 0;
++ goto out;
+ }
+
+ skb_list_walk_safe(skb, skb, next) {
+@@ -4435,9 +4492,11 @@ normal:
+ return NETDEV_TX_OK;
+ }
+
+-static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb, struct sta_info *sta,
+- bool txpending)
++
++
++static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct sta_info *sta,
++ bool txpending)
+ {
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_control control = {};
+@@ -4446,14 +4505,6 @@ static bool ieee80211_tx_8023(struct iee
+ unsigned long flags;
+ int q = info->hw_queue;
+
+- if (sta)
+- sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift);
+-
+- ieee80211_tpt_led_trig_tx(local, skb->len);
+-
+- if (ieee80211_queue_skb(local, sdata, sta, skb))
+- return true;
+-
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+ if (local->queue_stop_reasons[q] ||
+@@ -4480,6 +4531,26 @@ static bool ieee80211_tx_8023(struct iee
+ return true;
+ }
+
++static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct sta_info *sta,
++ bool txpending)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct sk_buff *next;
++ bool ret = true;
++
++ if (ieee80211_queue_skb(local, sdata, sta, skb))
++ return true;
++
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ if (!__ieee80211_tx_8023(sdata, skb, sta, txpending))
++ ret = false;
++ }
++
++ return ret;
++}
++
+ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev, struct sta_info *sta,
+ struct ieee80211_key *key, struct sk_buff *skb)
+@@ -4487,9 +4558,13 @@ static void ieee80211_8023_xmit(struct i
+ struct ieee80211_tx_info *info;
+ struct ieee80211_local *local = sdata->local;
+ struct tid_ampdu_tx *tid_tx;
++ struct sk_buff *seg, *next;
++ unsigned int skbs = 0, len = 0;
++ u16 queue;
+ u8 tid;
+
+- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
++ queue = ieee80211_select_queue(sdata, sta, skb);
++ skb_set_queue_mapping(skb, queue);
+
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+@@ -4499,9 +4574,6 @@ static void ieee80211_8023_xmit(struct i
+ if (unlikely(!skb))
+ return;
+
+- info = IEEE80211_SKB_CB(skb);
+- memset(info, 0, sizeof(*info));
+-
+ ieee80211_aggr_check(sdata, sta, skb);
+
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+@@ -4515,22 +4587,20 @@ static void ieee80211_8023_xmit(struct i
+ return;
+ }
+
+- info->flags |= IEEE80211_TX_CTL_AMPDU;
+ if (tid_tx->timeout)
+ tid_tx->last_tx = jiffies;
+ }
+
+- if (unlikely(skb->sk &&
+- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+- &info->flags, NULL);
++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
++ if (!skb)
++ return;
+
+- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
++ info = IEEE80211_SKB_CB(skb);
++ memset(info, 0, sizeof(*info));
++ if (tid_tx)
++ info->flags |= IEEE80211_TX_CTL_AMPDU;
+
+- dev_sw_netstats_tx_add(dev, 1, skb->len);
+-
+- sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+- sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++;
++ info->hw_queue = sdata->vif.hw_queue[queue];
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+@@ -4542,6 +4612,24 @@ static void ieee80211_8023_xmit(struct i
+ if (key)
+ info->control.hw_key = &key->conf;
+
++ skb_list_walk_safe(skb, seg, next) {
++ skbs++;
++ len += seg->len;
++ if (seg != skb)
++ memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info));
++ }
++
++ if (unlikely(skb->sk &&
++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
++ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
++ &info->flags, NULL);
++
++ dev_sw_netstats_tx_add(dev, skbs, len);
++ sta->deflink.tx_stats.packets[queue] += skbs;
++ sta->deflink.tx_stats.bytes[queue] += len;
++
++ ieee80211_tpt_led_trig_tx(local, len);
++
+ ieee80211_tx_8023(sdata, skb, sta, false);
+
+ return;
+@@ -4583,6 +4671,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
+ goto skip_offload;
+
++ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
+ ieee80211_8023_xmit(sdata, dev, sta, key, skb);
+ goto out;
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 6 Feb 2021 16:08:01 +0100
-Subject: [PATCH] mac80211: minstrel_ht: reduce fluctuations in rate
- probability stats
-
-In some scenarios when there is a lot of fluctuation in packet error rates,
-rate switching can be amplified when the statistics get skewed by time slots
-with very few tries.
-Make the input data to the moving average more smooth by adding the
-success/attempts count from the last stats window as well. This has the
-advantage of smoothing the data without introducing any extra lag to sampling
-rates.
-This significantly improves rate stability on a strong test link subjected to
-periodic noise bursts generated with a SDR
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -703,7 +703,8 @@ minstrel_ht_calc_rate_stats(struct minst
- unsigned int cur_prob;
-
- if (unlikely(mrs->attempts > 0)) {
-- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
-+ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
-+ mrs->attempts + mrs->last_attempts);
- minstrel_filter_avg_add(&mrs->prob_avg,
- &mrs->prob_avg_1, cur_prob);
- mrs->att_hist += mrs->attempts;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 6 Feb 2021 16:33:14 +0100
-Subject: [PATCH] mac80211: minstrel_ht: rework rate downgrade code and
- max_prob rate selection
-
-The current fallback code for fast rate switching on potentially failing rates
-is triggering too often if there is some strong noise on the channel. This can
-lead to wild fluctuations in the rate selection.
-Additionally, switching down to max_prob_rate can create a significant gap down
-in throughput, especially when using only 2 spatial streams, because max_prob_rate
-is limited to using fewer streams than the max_tp rates.
-In order to improve throughput without reducing reliability too much, use the
-rate downgrade code for the max_prob_rate only, and allow the non-downgraded
-max_prob_rate to use as many spatial streams as the max_tp rates
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rc80211_minstrel_ht.c
-+++ b/net/mac80211/rc80211_minstrel_ht.c
-@@ -514,6 +514,14 @@ minstrel_ht_set_best_prob_rate(struct mi
- int cur_tp_avg, cur_group, cur_idx;
- int max_gpr_group, max_gpr_idx;
- int max_gpr_tp_avg, max_gpr_prob;
-+ int min_dur;
-+
-+ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
-+ minstrel_get_duration(mi->max_tp_rate[1]));
-+
-+ /* make the rate at least 18% slower than max tp rates */
-+ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
-+ return;
-
- cur_group = MI_RATE_GROUP(index);
- cur_idx = MI_RATE_IDX(index);
-@@ -535,11 +543,6 @@ minstrel_ht_set_best_prob_rate(struct mi
- !minstrel_ht_is_legacy_group(max_tp_group))
- return;
-
-- /* skip rates faster than max tp rate with lower prob */
-- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
-- mrs->prob_avg < max_tp_prob)
-- return;
--
- max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
- max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
- max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
-@@ -597,40 +600,6 @@ minstrel_ht_assign_best_tp_rates(struct
-
- }
-
--/*
-- * Try to increase robustness of max_prob rate by decrease number of
-- * streams if possible.
-- */
--static inline void
--minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
--{
-- struct minstrel_mcs_group_data *mg;
-- int tmp_max_streams, group, tmp_idx, tmp_prob;
-- int tmp_tp = 0;
--
-- if (!mi->sta->ht_cap.ht_supported)
-- return;
--
-- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
-- tmp_max_streams = minstrel_mcs_groups[group].streams;
-- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
-- mg = &mi->groups[group];
-- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
-- continue;
--
-- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
-- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
--
-- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
-- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
-- mi->max_prob_rate = mg->max_group_prob_rate;
-- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
-- tmp_idx,
-- tmp_prob);
-- }
-- }
--}
--
- static u16
- __minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
- enum minstrel_sample_type type)
-@@ -1110,8 +1079,6 @@ minstrel_ht_update_stats(struct minstrel
-
- mi->max_prob_rate = tmp_max_prob_rate;
-
-- /* Try to increase robustness of max_prob_rate*/
-- minstrel_ht_prob_rate_reduce_streams(mi);
- minstrel_ht_refill_sample_rates(mi);
-
- #ifdef CPTCFG_MAC80211_DEBUGFS
-@@ -1156,7 +1123,7 @@ minstrel_ht_txstat_valid(struct minstrel
- }
-
- static void
--minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
-+minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
- {
- int group, orig_group;
-
-@@ -1171,11 +1138,7 @@ minstrel_downgrade_rate(struct minstrel_
- minstrel_mcs_groups[orig_group].streams)
- continue;
-
-- if (primary)
-- *idx = mi->groups[group].max_group_tp_rate[0];
-- else
-- *idx = mi->groups[group].max_group_tp_rate[1];
-- break;
-+ *idx = mi->groups[group].max_group_prob_rate;
- }
- }
-
-@@ -1186,7 +1149,7 @@ minstrel_ht_tx_status(void *priv, struct
- struct ieee80211_tx_info *info = st->info;
- struct minstrel_ht_sta *mi = priv_sta;
- struct ieee80211_tx_rate *ar = info->status.rates;
-- struct minstrel_rate_stats *rate, *rate2;
-+ struct minstrel_rate_stats *rate;
- struct minstrel_priv *mp = priv;
- u32 update_interval = mp->update_interval;
- bool last, update = false;
-@@ -1236,18 +1199,13 @@ minstrel_ht_tx_status(void *priv, struct
- /*
- * check for sudden death of spatial multiplexing,
- * downgrade to a lower number of streams if necessary.
-+ * only do this for the max_prob_rate to prevent spurious
-+ * rate fluctuations when the link changes suddenly
- */
-- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
-+ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
- if (rate->attempts > 30 &&
- rate->success < rate->attempts / 4) {
-- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
-- update = true;
-- }
--
-- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
-- if (rate2->attempts > 30 &&
-- rate2->success < rate2->attempts / 4) {
-- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
-+ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
- update = true;
- }
- }
+++ /dev/null
-From: Aloka Dixit <alokad@codeaurora.org>
-Date: Tue, 5 Oct 2021 21:09:36 -0700
-Subject: [PATCH] mac80211: split beacon retrieval functions
-
-Split __ieee80211_beacon_get() into a separate function for AP mode
-ieee80211_beacon_get_ap().
-Also, move the code common to all modes (AP, adhoc and mesh) to
-a separate function ieee80211_beacon_get_finish().
-
-Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
-Link: https://lore.kernel.org/r/20211006040938.9531-2-alokad@codeaurora.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -4987,6 +4987,115 @@ static int ieee80211_beacon_protect(stru
- return 0;
- }
-
-+static void
-+ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif,
-+ struct ieee80211_mutable_offsets *offs,
-+ struct beacon_data *beacon,
-+ struct sk_buff *skb,
-+ struct ieee80211_chanctx_conf *chanctx_conf,
-+ u16 csa_off_base)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-+ struct ieee80211_tx_info *info;
-+ enum nl80211_band band;
-+ struct ieee80211_tx_rate_control txrc;
-+
-+ /* CSA offsets */
-+ if (offs && beacon) {
-+ u16 i;
-+
-+ for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) {
-+ u16 csa_off = beacon->cntdwn_counter_offsets[i];
-+
-+ if (!csa_off)
-+ continue;
-+
-+ offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
-+ }
-+ }
-+
-+ band = chanctx_conf->def.chan->band;
-+ info = IEEE80211_SKB_CB(skb);
-+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
-+ info->band = band;
-+
-+ memset(&txrc, 0, sizeof(txrc));
-+ txrc.hw = hw;
-+ txrc.sband = local->hw.wiphy->bands[band];
-+ txrc.bss_conf = &sdata->vif.bss_conf;
-+ txrc.skb = skb;
-+ txrc.reported_rate.idx = -1;
-+ if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
-+ txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band];
-+ else
-+ txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
-+ txrc.bss = true;
-+ rate_control_get_rate(sdata, NULL, &txrc);
-+
-+ info->control.vif = vif;
-+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
-+ IEEE80211_TX_CTL_ASSIGN_SEQ |
-+ IEEE80211_TX_CTL_FIRST_FRAGMENT;
-+}
-+
-+static struct sk_buff *
-+ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif,
-+ struct ieee80211_mutable_offsets *offs,
-+ bool is_template,
-+ struct beacon_data *beacon,
-+ struct ieee80211_chanctx_conf *chanctx_conf)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-+ struct ieee80211_if_ap *ap = &sdata->u.ap;
-+ struct sk_buff *skb = NULL;
-+ u16 csa_off_base = 0;
-+
-+ if (beacon->cntdwn_counter_offsets[0]) {
-+ if (!is_template)
-+ ieee80211_beacon_update_cntdwn(vif);
-+
-+ ieee80211_set_beacon_cntdwn(sdata, beacon);
-+ }
-+
-+ /* headroom, head length,
-+ * tail length and maximum TIM length
-+ */
-+ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
-+ beacon->tail_len + 256 +
-+ local->hw.extra_beacon_tailroom);
-+ if (!skb)
-+ return NULL;
-+
-+ skb_reserve(skb, local->tx_headroom);
-+ skb_put_data(skb, beacon->head, beacon->head_len);
-+
-+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb, is_template);
-+
-+ if (offs) {
-+ offs->tim_offset = beacon->head_len;
-+ offs->tim_length = skb->len - beacon->head_len;
-+ offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
-+
-+ /* for AP the csa offsets are from tail */
-+ csa_off_base = skb->len;
-+ }
-+
-+ if (beacon->tail)
-+ skb_put_data(skb, beacon->tail, beacon->tail_len);
-+
-+ if (ieee80211_beacon_protect(skb, local, sdata) < 0)
-+ return NULL;
-+
-+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, chanctx_conf,
-+ csa_off_base);
-+ return skb;
-+}
-+
- static struct sk_buff *
- __ieee80211_beacon_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
-@@ -4996,12 +5105,8 @@ __ieee80211_beacon_get(struct ieee80211_
- struct ieee80211_local *local = hw_to_local(hw);
- struct beacon_data *beacon = NULL;
- struct sk_buff *skb = NULL;
-- struct ieee80211_tx_info *info;
- struct ieee80211_sub_if_data *sdata = NULL;
-- enum nl80211_band band;
-- struct ieee80211_tx_rate_control txrc;
- struct ieee80211_chanctx_conf *chanctx_conf;
-- int csa_off_base = 0;
-
- rcu_read_lock();
-
-@@ -5018,48 +5123,11 @@ __ieee80211_beacon_get(struct ieee80211_
- struct ieee80211_if_ap *ap = &sdata->u.ap;
-
- beacon = rcu_dereference(ap->beacon);
-- if (beacon) {
-- if (beacon->cntdwn_counter_offsets[0]) {
-- if (!is_template)
-- ieee80211_beacon_update_cntdwn(vif);
--
-- ieee80211_set_beacon_cntdwn(sdata, beacon);
-- }
--
-- /*
-- * headroom, head length,
-- * tail length and maximum TIM length
-- */
-- skb = dev_alloc_skb(local->tx_headroom +
-- beacon->head_len +
-- beacon->tail_len + 256 +
-- local->hw.extra_beacon_tailroom);
-- if (!skb)
-- goto out;
--
-- skb_reserve(skb, local->tx_headroom);
-- skb_put_data(skb, beacon->head, beacon->head_len);
--
-- ieee80211_beacon_add_tim(sdata, &ap->ps, skb,
-- is_template);
--
-- if (offs) {
-- offs->tim_offset = beacon->head_len;
-- offs->tim_length = skb->len - beacon->head_len;
-- offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
--
-- /* for AP the csa offsets are from tail */
-- csa_off_base = skb->len;
-- }
--
-- if (beacon->tail)
-- skb_put_data(skb, beacon->tail,
-- beacon->tail_len);
--
-- if (ieee80211_beacon_protect(skb, local, sdata) < 0)
-- goto out;
-- } else
-+ if (!beacon)
- goto out;
-+
-+ skb = ieee80211_beacon_get_ap(hw, vif, offs, is_template,
-+ beacon, chanctx_conf);
- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- struct ieee80211_hdr *hdr;
-@@ -5085,6 +5153,9 @@ __ieee80211_beacon_get(struct ieee80211_
- hdr = (struct ieee80211_hdr *) skb->data;
- hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_BEACON);
-+
-+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
-+ chanctx_conf, 0);
- } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-
-@@ -5124,51 +5195,13 @@ __ieee80211_beacon_get(struct ieee80211_
- }
-
- skb_put_data(skb, beacon->tail, beacon->tail_len);
-+ ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
-+ chanctx_conf, 0);
- } else {
- WARN_ON(1);
- goto out;
- }
-
-- /* CSA offsets */
-- if (offs && beacon) {
-- int i;
--
-- for (i = 0; i < IEEE80211_MAX_CNTDWN_COUNTERS_NUM; i++) {
-- u16 csa_off = beacon->cntdwn_counter_offsets[i];
--
-- if (!csa_off)
-- continue;
--
-- offs->cntdwn_counter_offs[i] = csa_off_base + csa_off;
-- }
-- }
--
-- band = chanctx_conf->def.chan->band;
--
-- info = IEEE80211_SKB_CB(skb);
--
-- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-- info->flags |= IEEE80211_TX_CTL_NO_ACK;
-- info->band = band;
--
-- memset(&txrc, 0, sizeof(txrc));
-- txrc.hw = hw;
-- txrc.sband = local->hw.wiphy->bands[band];
-- txrc.bss_conf = &sdata->vif.bss_conf;
-- txrc.skb = skb;
-- txrc.reported_rate.idx = -1;
-- if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
-- txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band];
-- else
-- txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
-- txrc.bss = true;
-- rate_control_get_rate(sdata, NULL, &txrc);
--
-- info->control.vif = vif;
--
-- info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
-- IEEE80211_TX_CTL_ASSIGN_SEQ |
-- IEEE80211_TX_CTL_FIRST_FRAGMENT;
- out:
- rcu_read_unlock();
- return skb;
+++ /dev/null
-From: John Crispin <john@phrozen.org>
-Date: Wed, 15 Sep 2021 19:54:34 -0700
-Subject: [PATCH] nl80211: MBSSID and EMA support in AP mode
-
-Add new attributes to configure support for multiple BSSID
-and advanced multi-BSSID advertisements (EMA) in AP mode.
-
-- NL80211_ATTR_MBSSID_CONFIG used for per interface configuration.
-- NL80211_ATTR_MBSSID_ELEMS used to MBSSID elements for beacons.
-
-Memory for the elements is allocated dynamically. This change frees
-the memory in existing functions which call nl80211_parse_beacon(),
-a comment is added to indicate the new references to do the same.
-
-Signed-off-by: John Crispin <john@phrozen.org>
-Co-developed-by: Aloka Dixit <alokad@codeaurora.org>
-Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
-Link: https://lore.kernel.org/r/20210916025437.29138-2-alokad@codeaurora.org
-[don't leave ERR_PTR hanging around]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -1046,6 +1046,36 @@ struct cfg80211_crypto_settings {
- };
-
- /**
-+ * struct cfg80211_mbssid_config - AP settings for multi bssid
-+ *
-+ * @tx_wdev: pointer to the transmitted interface in the MBSSID set
-+ * @index: index of this AP in the multi bssid group.
-+ * @ema: set to true if the beacons should be sent out in EMA mode.
-+ */
-+struct cfg80211_mbssid_config {
-+ struct wireless_dev *tx_wdev;
-+ u8 index;
-+ bool ema;
-+};
-+
-+/**
-+ * struct cfg80211_mbssid_elems - Multiple BSSID elements
-+ *
-+ * @cnt: Number of elements in array %elems.
-+ *
-+ * @elem: Array of multiple BSSID element(s) to be added into Beacon frames.
-+ * @elem.data: Data for multiple BSSID elements.
-+ * @elem.len: Length of data.
-+ */
-+struct cfg80211_mbssid_elems {
-+ u8 cnt;
-+ struct {
-+ const u8 *data;
-+ size_t len;
-+ } elem[];
-+};
-+
-+/**
- * struct cfg80211_beacon_data - beacon data
- * @head: head portion of beacon (before TIM IE)
- * or %NULL if not changed
-@@ -1063,6 +1093,7 @@ struct cfg80211_crypto_settings {
- * @assocresp_ies_len: length of assocresp_ies in octets
- * @probe_resp_len: length of probe response template (@probe_resp)
- * @probe_resp: probe response template (AP mode only)
-+ * @mbssid_ies: multiple BSSID elements
- * @ftm_responder: enable FTM responder functionality; -1 for no change
- * (which also implies no change in LCI/civic location data)
- * @lci: Measurement Report element content, starting with Measurement Token
-@@ -1080,6 +1111,7 @@ struct cfg80211_beacon_data {
- const u8 *probe_resp;
- const u8 *lci;
- const u8 *civicloc;
-+ struct cfg80211_mbssid_elems *mbssid_ies;
- s8 ftm_responder;
-
- size_t head_len, tail_len;
-@@ -1194,6 +1226,7 @@ enum cfg80211_ap_settings_flags {
- * @he_oper: HE operation IE (or %NULL if HE isn't enabled)
- * @fils_discovery: FILS discovery transmission parameters
- * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
-+ * @mbssid_config: AP settings for multiple bssid
- */
- struct cfg80211_ap_settings {
- struct cfg80211_chan_def chandef;
-@@ -1226,6 +1259,7 @@ struct cfg80211_ap_settings {
- struct cfg80211_he_bss_color he_bss_color;
- struct cfg80211_fils_discovery fils_discovery;
- struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
-+ struct cfg80211_mbssid_config mbssid_config;
- };
-
- /**
-@@ -4986,6 +5020,13 @@ struct wiphy_iftype_akm_suites {
- * %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
- * @sar_capa: SAR control capabilities
- * @rfkill: a pointer to the rfkill structure
-+ *
-+ * @mbssid_max_interfaces: maximum number of interfaces supported by the driver
-+ * in a multiple BSSID set. This field must be set to a non-zero value
-+ * by the driver to advertise MBSSID support.
-+ * @mbssid_max_ema_profile_periodicity: maximum profile periodicity supported by
-+ * the driver. Setting this field to a non-zero value indicates that the
-+ * driver supports enhanced multi-BSSID advertisements (EMA AP).
- */
- struct wiphy {
- struct mutex mtx;
-@@ -5133,6 +5174,9 @@ struct wiphy {
-
- struct rfkill *rfkill;
-
-+ u8 mbssid_max_interfaces;
-+ u8 ema_max_profile_periodicity;
-+
- char priv[] __aligned(NETDEV_ALIGN);
- };
-
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -337,7 +337,10 @@
- * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
- * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
- * userspace to request deletion of a virtual interface, then requires
-- * attribute %NL80211_ATTR_IFINDEX.
-+ * attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are
-+ * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
-+ * and if this command is used for the transmitting interface, then all
-+ * the non-transmitting interfaces are deleted as well.
- *
- * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
- * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
-@@ -2593,6 +2596,18 @@ enum nl80211_commands {
- * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
- * information for the time while performing a color switch.
- *
-+ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
-+ * advertisements (MBSSID) parameters in AP mode.
-+ * Kernel uses this attribute to indicate the driver's support for MBSSID
-+ * and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
-+ * Userspace should use this attribute to configure per interface MBSSID
-+ * parameters.
-+ * See &enum nl80211_mbssid_config_attributes for details.
-+ *
-+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
-+ * Mandatory parameter for the transmitting interface to enable MBSSID.
-+ * Optional for the non-transmitting interfaces.
-+ *
- * @NUM_NL80211_ATTR: total number of nl80211_attrs available
- * @NL80211_ATTR_MAX: highest attribute number currently defined
- * @__NL80211_ATTR_AFTER_LAST: internal use
-@@ -3096,6 +3111,9 @@ enum nl80211_attrs {
- NL80211_ATTR_COLOR_CHANGE_COLOR,
- NL80211_ATTR_COLOR_CHANGE_ELEMS,
-
-+ NL80211_ATTR_MBSSID_CONFIG,
-+ NL80211_ATTR_MBSSID_ELEMS,
-+
- /* add attributes here, update the policy in nl80211.c */
-
- __NL80211_ATTR_AFTER_LAST,
-@@ -7349,4 +7367,60 @@ enum nl80211_sar_specs_attrs {
- NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
- };
-
-+/**
-+ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
-+ * multi-BSSID advertisements (EMA) in AP mode.
-+ * Kernel uses some of these attributes to advertise driver's support for
-+ * MBSSID and EMA.
-+ * Remaining attributes should be used by the userspace to configure the
-+ * features.
-+ *
-+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
-+ *
-+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
-+ * the maximum number of MBSSID interfaces supported by the driver.
-+ * Driver should indicate MBSSID support by setting
-+ * wiphy->mbssid_max_interfaces to a value more than or equal to 2.
-+ *
-+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
-+ * to advertise the maximum profile periodicity supported by the driver
-+ * if EMA is enabled. Driver should indicate EMA support to the userspace
-+ * by setting wiphy->mbssid_max_ema_profile_periodicity to
-+ * a non-zero value.
-+ *
-+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
-+ * this BSS (u8) in the multiple BSSID set.
-+ * Value must be set to 0 for the transmitting interface and non-zero for
-+ * all non-transmitting interfaces. The userspace will be responsible
-+ * for using unique indices for the interfaces.
-+ * Range: 0 to wiphy->mbssid_max_interfaces-1.
-+ *
-+ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
-+ * a non-transmitted profile which provides the interface index (u32) of
-+ * the transmitted profile. The value must match one of the interface
-+ * indices advertised by the kernel. Optional if the interface being set up
-+ * is the transmitting one, however, if provided then the value must match
-+ * the interface index of the same.
-+ *
-+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
-+ * Setting this flag is permitted only if the driver advertises EMA support
-+ * by setting wiphy->mbssid_max_ema_profile_periodicity to non-zero.
-+ *
-+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
-+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
-+ */
-+enum nl80211_mbssid_config_attributes {
-+ __NL80211_MBSSID_CONFIG_ATTR_INVALID,
-+
-+ NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
-+ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
-+ NL80211_MBSSID_CONFIG_ATTR_INDEX,
-+ NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
-+ NL80211_MBSSID_CONFIG_ATTR_EMA,
-+
-+ /* keep last */
-+ __NL80211_MBSSID_CONFIG_ATTR_LAST,
-+ NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
-+};
-+
- #endif /* __LINUX_NL80211_H */
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -442,6 +442,16 @@ sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
- [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
- };
-
-+static const struct nla_policy
-+nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
-+ [NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2),
-+ [NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] =
-+ NLA_POLICY_MIN(NLA_U8, 1),
-+ [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 },
-+ [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
-+ [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
-+};
-+
- static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
- [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
- [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
-@@ -788,6 +798,9 @@ static const struct nla_policy nl80211_p
- [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
- [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
- [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
-+ [NL80211_ATTR_MBSSID_CONFIG] =
-+ NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
-+ [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
- };
-
- /* policy for the key attributes */
-@@ -2236,6 +2249,35 @@ fail:
- return -ENOBUFS;
- }
-
-+static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg)
-+{
-+ struct nlattr *config;
-+
-+ if (!wiphy->mbssid_max_interfaces)
-+ return 0;
-+
-+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
-+ if (!config)
-+ return -ENOBUFS;
-+
-+ if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
-+ wiphy->mbssid_max_interfaces))
-+ goto fail;
-+
-+ if (wiphy->ema_max_profile_periodicity &&
-+ nla_put_u8(msg,
-+ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
-+ wiphy->ema_max_profile_periodicity))
-+ goto fail;
-+
-+ nla_nest_end(msg, config);
-+ return 0;
-+
-+fail:
-+ nla_nest_cancel(msg, config);
-+ return -ENOBUFS;
-+}
-+
- struct nl80211_dump_wiphy_state {
- s64 filter_wiphy;
- long start;
-@@ -2821,6 +2863,9 @@ static int nl80211_send_wiphy(struct cfg
- if (nl80211_put_sar_specs(rdev, msg))
- goto nla_put_failure;
-
-+ if (nl80211_put_mbssid_support(&rdev->wiphy, msg))
-+ goto nla_put_failure;
-+
- /* done */
- state->split_start = 0;
- break;
-@@ -5020,6 +5065,96 @@ static int validate_beacon_tx_rate(struc
- return 0;
- }
-
-+static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
-+ struct net_device *dev,
-+ struct nlattr *attrs,
-+ struct cfg80211_mbssid_config *config,
-+ u8 num_elems)
-+{
-+ struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
-+
-+ if (!wiphy->mbssid_max_interfaces)
-+ return -EOPNOTSUPP;
-+
-+ if (nla_parse_nested(tb, NL80211_MBSSID_CONFIG_ATTR_MAX, attrs, NULL,
-+ NULL) ||
-+ !tb[NL80211_MBSSID_CONFIG_ATTR_INDEX])
-+ return -EINVAL;
-+
-+ config->ema = nla_get_flag(tb[NL80211_MBSSID_CONFIG_ATTR_EMA]);
-+ if (config->ema) {
-+ if (!wiphy->ema_max_profile_periodicity)
-+ return -EOPNOTSUPP;
-+
-+ if (num_elems > wiphy->ema_max_profile_periodicity)
-+ return -EINVAL;
-+ }
-+
-+ config->index = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]);
-+ if (config->index >= wiphy->mbssid_max_interfaces ||
-+ (!config->index && !num_elems))
-+ return -EINVAL;
-+
-+ if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) {
-+ u32 tx_ifindex =
-+ nla_get_u32(tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]);
-+
-+ if ((!config->index && tx_ifindex != dev->ifindex) ||
-+ (config->index && tx_ifindex == dev->ifindex))
-+ return -EINVAL;
-+
-+ if (tx_ifindex != dev->ifindex) {
-+ struct net_device *tx_netdev =
-+ dev_get_by_index(wiphy_net(wiphy), tx_ifindex);
-+
-+ if (!tx_netdev || !tx_netdev->ieee80211_ptr ||
-+ tx_netdev->ieee80211_ptr->wiphy != wiphy ||
-+ tx_netdev->ieee80211_ptr->iftype !=
-+ NL80211_IFTYPE_AP) {
-+ dev_put(tx_netdev);
-+ return -EINVAL;
-+ }
-+
-+ config->tx_wdev = tx_netdev->ieee80211_ptr;
-+ } else {
-+ config->tx_wdev = dev->ieee80211_ptr;
-+ }
-+ } else if (!config->index) {
-+ config->tx_wdev = dev->ieee80211_ptr;
-+ } else {
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static struct cfg80211_mbssid_elems *
-+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
-+{
-+ struct nlattr *nl_elems;
-+ struct cfg80211_mbssid_elems *elems;
-+ int rem_elems;
-+ u8 i = 0, num_elems = 0;
-+
-+ if (!wiphy->mbssid_max_interfaces)
-+ return ERR_PTR(-EINVAL);
-+
-+ nla_for_each_nested(nl_elems, attrs, rem_elems)
-+ num_elems++;
-+
-+ elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL);
-+ if (!elems)
-+ return ERR_PTR(-ENOMEM);
-+
-+ nla_for_each_nested(nl_elems, attrs, rem_elems) {
-+ elems->elem[i].data = nla_data(nl_elems);
-+ elems->elem[i].len = nla_len(nl_elems);
-+ i++;
-+ }
-+ elems->cnt = num_elems;
-+ return elems;
-+}
-+
- static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
- struct nlattr *attrs[],
- struct cfg80211_beacon_data *bcn)
-@@ -5100,6 +5235,17 @@ static int nl80211_parse_beacon(struct c
- bcn->ftm_responder = -1;
- }
-
-+ if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
-+ struct cfg80211_mbssid_elems *mbssid =
-+ nl80211_parse_mbssid_elems(&rdev->wiphy,
-+ attrs[NL80211_ATTR_MBSSID_ELEMS]);
-+
-+ if (IS_ERR(mbssid))
-+ return PTR_ERR(mbssid);
-+
-+ bcn->mbssid_ies = mbssid;
-+ }
-+
- return 0;
- }
-
-@@ -5556,6 +5702,17 @@ static int nl80211_start_ap(struct sk_bu
- goto out;
- }
-
-+ if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) {
-+ err = nl80211_parse_mbssid_config(&rdev->wiphy, dev,
-+ info->attrs[NL80211_ATTR_MBSSID_CONFIG],
-+ ¶ms.mbssid_config,
-+ params.beacon.mbssid_ies ?
-+ params.beacon.mbssid_ies->cnt :
-+ 0);
-+ if (err)
-+ goto out;
-+ }
-+
- nl80211_calculate_ap_params(¶ms);
-
- if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
-@@ -5577,6 +5734,11 @@ static int nl80211_start_ap(struct sk_bu
-
- out:
- kfree(params.acl);
-+ kfree(params.beacon.mbssid_ies);
-+ if (params.mbssid_config.tx_wdev &&
-+ params.mbssid_config.tx_wdev->netdev &&
-+ params.mbssid_config.tx_wdev->netdev != dev)
-+ dev_put(params.mbssid_config.tx_wdev->netdev);
-
- return err;
- }
-@@ -5601,12 +5763,14 @@ static int nl80211_set_beacon(struct sk_
-
- err = nl80211_parse_beacon(rdev, info->attrs, ¶ms);
- if (err)
-- return err;
-+ goto out;
-
- wdev_lock(wdev);
- err = rdev_change_beacon(rdev, dev, ¶ms);
- wdev_unlock(wdev);
-
-+out:
-+ kfree(params.mbssid_ies);
- return err;
- }
-
-@@ -9283,12 +9447,14 @@ static int nl80211_channel_switch(struct
-
- err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after);
- if (err)
-- return err;
-+ goto free;
-
- csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
- GFP_KERNEL);
-- if (!csa_attrs)
-- return -ENOMEM;
-+ if (!csa_attrs) {
-+ err = -ENOMEM;
-+ goto free;
-+ }
-
- err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
- info->attrs[NL80211_ATTR_CSA_IES],
-@@ -9407,6 +9573,8 @@ skip_beacons:
- wdev_unlock(wdev);
-
- free:
-+ kfree(params.beacon_after.mbssid_ies);
-+ kfree(params.beacon_csa.mbssid_ies);
- kfree(csa_attrs);
- return err;
- }
-@@ -14959,6 +15127,8 @@ static int nl80211_color_change(struct s
- wdev_unlock(wdev);
-
- out:
-+ kfree(params.beacon_next.mbssid_ies);
-+ kfree(params.beacon_color_change.mbssid_ies);
- kfree(tb);
- return err;
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 23 Oct 2021 11:10:50 +0200
-Subject: [PATCH] cfg80211: implement APIs for dedicated radar detection HW
-
-If a dedicated (off-channel) radar detection hardware (chain)
-is available in the hardware/driver, allow this to be used by
-calling the NL80211_CMD_RADAR_DETECT command with a new flag
-attribute requesting off-channel radar detection is used.
-
-Offchannel CAC (channel availability check) avoids the CAC
-downtime when switching to a radar channel or when turning on
-the AP.
-
-Drivers advertise support for this using the new feature flag
-NL80211_EXT_FEATURE_RADAR_OFFCHAN.
-
-Tested-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/7468e291ef5d05d692c1738d25b8f778d8ea5c3f.1634979655.git.lorenzo@kernel.org
-Link: https://lore.kernel.org/r/1e60e60fef00e14401adae81c3d49f3e5f307537.1634979655.git.lorenzo@kernel.org
-Link: https://lore.kernel.org/r/85fa50f57fc3adb2934c8d9ca0be30394de6b7e8.1634979655.git.lorenzo@kernel.org
-Link: https://lore.kernel.org/r/4b6c08671ad59aae0ac46fc94c02f31b1610eb72.1634979655.git.lorenzo@kernel.org
-Link: https://lore.kernel.org/r/241849ccaf2c228873c6f8495bf87b19159ba458.1634979655.git.lorenzo@kernel.org
-[remove offchan_mutex, fix cfg80211_stop_offchan_radar_detection(),
- remove gfp_t argument, fix documentation, fix tracing]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4057,6 +4057,15 @@ struct mgmt_frame_regs {
- * @set_sar_specs: Update the SAR (TX power) settings.
- *
- * @color_change: Initiate a color change.
-+ *
-+ * @set_radar_offchan: Configure dedicated offchannel chain available for
-+ * radar/CAC detection on some hw. This chain can't be used to transmit
-+ * or receive frames and it is bounded to a running wdev.
-+ * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * switching to a different channel during CAC detection on the selected
-+ * radar channel.
-+ * The caller is expected to set chandef pointer to NULL in order to
-+ * disable offchannel CAC/radar detection.
- */
- struct cfg80211_ops {
- int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
-@@ -4387,6 +4396,8 @@ struct cfg80211_ops {
- int (*color_change)(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_color_change_settings *params);
-+ int (*set_radar_offchan)(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef);
- };
-
- /*
-@@ -7608,6 +7619,20 @@ void cfg80211_cac_event(struct net_devic
- const struct cfg80211_chan_def *chandef,
- enum nl80211_radar_event event, gfp_t gfp);
-
-+/**
-+ * cfg80211_offchan_cac_event - Channel Availability Check (CAC) offchan event
-+ * @wiphy: the wiphy
-+ * @chandef: chandef for the current channel
-+ * @event: type of event
-+ *
-+ * This function is called when a Channel Availability Check (CAC) is finished,
-+ * started or aborted by a offchannel dedicated chain.
-+ *
-+ * Note that this acquires the wiphy lock.
-+ */
-+void cfg80211_offchan_cac_event(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event);
-
- /**
- * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -2608,6 +2608,13 @@ enum nl80211_commands {
- * Mandatory parameter for the transmitting interface to enable MBSSID.
- * Optional for the non-transmitting interfaces.
- *
-+ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for
-+ * radar/CAC detection on some hw. This chain can't be used to transmit
-+ * or receive frames and it is bounded to a running wdev.
-+ * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * switching on a different channel during CAC detection on the selected
-+ * radar channel.
-+ *
- * @NUM_NL80211_ATTR: total number of nl80211_attrs available
- * @NL80211_ATTR_MAX: highest attribute number currently defined
- * @__NL80211_ATTR_AFTER_LAST: internal use
-@@ -3114,6 +3121,8 @@ enum nl80211_attrs {
- NL80211_ATTR_MBSSID_CONFIG,
- NL80211_ATTR_MBSSID_ELEMS,
-
-+ NL80211_ATTR_RADAR_OFFCHAN,
-+
- /* add attributes here, update the policy in nl80211.c */
-
- __NL80211_ATTR_AFTER_LAST,
-@@ -6013,6 +6022,9 @@ enum nl80211_feature_flags {
- * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
- * detection and change announcemnts.
- *
-+ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
-+ * detection.
-+ *
- * @NUM_NL80211_EXT_FEATURES: number of extended features.
- * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
- */
-@@ -6078,6 +6090,7 @@ enum nl80211_ext_feature_index {
- NL80211_EXT_FEATURE_SECURE_RTT,
- NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
- NL80211_EXT_FEATURE_BSS_COLOR,
-+ NL80211_EXT_FEATURE_RADAR_OFFCHAN,
-
- /* add new features before the definition below */
- NUM_NL80211_EXT_FEATURES,
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -543,6 +543,7 @@ use_default_name:
- INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
- INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
- INIT_WORK(&rdev->event_work, cfg80211_event_work);
-+ INIT_DELAYED_WORK(&rdev->offchan_cac_work, cfg80211_offchan_cac_work);
-
- init_waitqueue_head(&rdev->dev_wait);
-
-@@ -1205,6 +1206,8 @@ void __cfg80211_leave(struct cfg80211_re
-
- cfg80211_pmsr_wdev_down(wdev);
-
-+ cfg80211_stop_offchan_radar_detection(wdev);
-+
- switch (wdev->iftype) {
- case NL80211_IFTYPE_ADHOC:
- __cfg80211_leave_ibss(rdev, dev, true);
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -84,6 +84,10 @@ struct cfg80211_registered_device {
-
- struct delayed_work dfs_update_channels_wk;
-
-+ struct wireless_dev *offchan_radar_wdev;
-+ struct cfg80211_chan_def offchan_radar_chandef;
-+ struct delayed_work offchan_cac_work;
-+
- /* netlink port which started critical protocol (0 means not started) */
- u32 crit_proto_nlportid;
-
-@@ -491,6 +495,15 @@ cfg80211_chandef_dfs_cac_time(struct wip
-
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
-+int
-+cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ struct cfg80211_chan_def *chandef);
-+
-+void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev);
-+
-+void cfg80211_offchan_cac_work(struct work_struct *work);
-+
- bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan);
-
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -970,3 +970,116 @@ void cfg80211_cac_event(struct net_devic
- nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
- }
- EXPORT_SYMBOL(cfg80211_cac_event);
-+
-+void cfg80211_offchan_cac_work(struct work_struct *work)
-+{
-+ struct delayed_work *delayed_work = to_delayed_work(work);
-+ struct cfg80211_registered_device *rdev;
-+
-+ rdev = container_of(delayed_work, struct cfg80211_registered_device,
-+ offchan_cac_work);
-+ cfg80211_offchan_cac_event(&rdev->wiphy, &rdev->offchan_radar_chandef,
-+ NL80211_RADAR_CAC_FINISHED);
-+}
-+
-+static void
-+__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
-+{
-+ struct wiphy *wiphy = &rdev->wiphy;
-+ struct net_device *netdev;
-+
-+ lockdep_assert_wiphy(&rdev->wiphy);
-+
-+ if (event != NL80211_RADAR_CAC_STARTED && !rdev->offchan_radar_wdev)
-+ return;
-+
-+ switch (event) {
-+ case NL80211_RADAR_CAC_FINISHED:
-+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
-+ memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
-+ queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
-+ cfg80211_sched_dfs_chan_update(rdev);
-+ wdev = rdev->offchan_radar_wdev;
-+ rdev->offchan_radar_wdev = NULL;
-+ break;
-+ case NL80211_RADAR_CAC_ABORTED:
-+ cancel_delayed_work(&rdev->offchan_cac_work);
-+ wdev = rdev->offchan_radar_wdev;
-+ rdev->offchan_radar_wdev = NULL;
-+ break;
-+ case NL80211_RADAR_CAC_STARTED:
-+ WARN_ON(!wdev);
-+ rdev->offchan_radar_wdev = wdev;
-+ break;
-+ default:
-+ return;
-+ }
-+
-+ netdev = wdev ? wdev->netdev : NULL;
-+ nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
-+}
-+
-+void cfg80211_offchan_cac_event(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
-+{
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+
-+ wiphy_lock(wiphy);
-+ __cfg80211_offchan_cac_event(rdev, NULL, chandef, event);
-+ wiphy_unlock(wiphy);
-+}
-+EXPORT_SYMBOL(cfg80211_offchan_cac_event);
-+
-+int
-+cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ struct cfg80211_chan_def *chandef)
-+{
-+ unsigned int cac_time_ms;
-+ int err;
-+
-+ lockdep_assert_wiphy(&rdev->wiphy);
-+
-+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
-+ NL80211_EXT_FEATURE_RADAR_OFFCHAN))
-+ return -EOPNOTSUPP;
-+
-+ if (rdev->offchan_radar_wdev)
-+ return -EBUSY;
-+
-+ err = rdev_set_radar_offchan(rdev, chandef);
-+ if (err)
-+ return err;
-+
-+ cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef);
-+ if (!cac_time_ms)
-+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-+
-+ rdev->offchan_radar_chandef = *chandef;
-+ __cfg80211_offchan_cac_event(rdev, wdev, chandef,
-+ NL80211_RADAR_CAC_STARTED);
-+ queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_work,
-+ msecs_to_jiffies(cac_time_ms));
-+
-+ return 0;
-+}
-+
-+void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev)
-+{
-+ struct wiphy *wiphy = wdev->wiphy;
-+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-+
-+ lockdep_assert_wiphy(wiphy);
-+
-+ if (wdev != rdev->offchan_radar_wdev)
-+ return;
-+
-+ rdev_set_radar_offchan(rdev, NULL);
-+
-+ __cfg80211_offchan_cac_event(rdev, NULL, NULL,
-+ NL80211_RADAR_CAC_ABORTED);
-+}
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -801,6 +801,7 @@ static const struct nla_policy nl80211_p
- [NL80211_ATTR_MBSSID_CONFIG] =
- NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
- [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
-+ [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG },
- };
-
- /* policy for the key attributes */
-@@ -9287,12 +9288,6 @@ static int nl80211_start_radar_detection
- if (err)
- return err;
-
-- if (netif_carrier_ok(dev))
-- return -EBUSY;
--
-- if (wdev->cac_started)
-- return -EBUSY;
--
- err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
- if (err < 0)
- return err;
-@@ -9303,6 +9298,16 @@ static int nl80211_start_radar_detection
- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
- return -EINVAL;
-
-+ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN]))
-+ return cfg80211_start_offchan_radar_detection(rdev, wdev,
-+ &chandef);
-+
-+ if (netif_carrier_ok(dev))
-+ return -EBUSY;
-+
-+ if (wdev->cac_started)
-+ return -EBUSY;
-+
- /* CAC start is offloaded to HW and can't be started manually */
- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
- return -EOPNOTSUPP;
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(stru
- return ret;
- }
-
-+static inline int
-+rdev_set_radar_offchan(struct cfg80211_registered_device *rdev,
-+ struct cfg80211_chan_def *chandef)
-+{
-+ struct wiphy *wiphy = &rdev->wiphy;
-+ int ret;
-+
-+ if (!rdev->ops->set_radar_offchan)
-+ return -EOPNOTSUPP;
-+
-+ trace_rdev_set_radar_offchan(wiphy, chandef);
-+ ret = rdev->ops->set_radar_offchan(wiphy, chandef);
-+ trace_rdev_return_int(wiphy, ret);
-+
-+ return ret;
-+}
-+
- #endif /* __CFG80211_RDEV_OPS */
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -3643,6 +3643,25 @@ TRACE_EVENT(cfg80211_bss_color_notify,
- __entry->color_bitmap)
- );
-
-+TRACE_EVENT(rdev_set_radar_offchan,
-+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-+
-+ TP_ARGS(wiphy, chandef),
-+
-+ TP_STRUCT__entry(
-+ WIPHY_ENTRY
-+ CHAN_DEF_ENTRY
-+ ),
-+
-+ TP_fast_assign(
-+ WIPHY_ASSIGN;
-+ CHAN_DEF_ASSIGN(chandef)
-+ ),
-+
-+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
-+);
-+
- #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
-
- #undef TRACE_INCLUDE_PATH
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 27 Oct 2021 11:03:42 +0200
-Subject: [PATCH] cfg80211: move offchan_cac_event to a dedicated work
-
-In order to make cfg80211_offchan_cac_abort() (renamed from
-cfg80211_offchan_cac_event) callable in other contexts and
-without so much locking restrictions, make it trigger a new
-work instead of operating directly.
-
-Do some other renames while at it to clarify.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/6145c3d0f30400a568023f67981981d24c7c6133.1635325205.git.lorenzo@kernel.org
-[rewrite commit log]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -7620,19 +7620,13 @@ void cfg80211_cac_event(struct net_devic
- enum nl80211_radar_event event, gfp_t gfp);
-
- /**
-- * cfg80211_offchan_cac_event - Channel Availability Check (CAC) offchan event
-+ * cfg80211_offchan_cac_abort - Channel Availability Check offchan abort event
- * @wiphy: the wiphy
-- * @chandef: chandef for the current channel
-- * @event: type of event
- *
-- * This function is called when a Channel Availability Check (CAC) is finished,
-- * started or aborted by a offchannel dedicated chain.
-- *
-- * Note that this acquires the wiphy lock.
-+ * This function is called by the driver when a Channel Availability Check
-+ * (CAC) is aborted by a offchannel dedicated chain.
- */
--void cfg80211_offchan_cac_event(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event);
-+void cfg80211_offchan_cac_abort(struct wiphy *wiphy);
-
- /**
- * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -543,7 +543,9 @@ use_default_name:
- INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
- INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
- INIT_WORK(&rdev->event_work, cfg80211_event_work);
-- INIT_DELAYED_WORK(&rdev->offchan_cac_work, cfg80211_offchan_cac_work);
-+ INIT_WORK(&rdev->offchan_cac_abort_wk, cfg80211_offchan_cac_abort_wk);
-+ INIT_DELAYED_WORK(&rdev->offchan_cac_done_wk,
-+ cfg80211_offchan_cac_done_wk);
-
- init_waitqueue_head(&rdev->dev_wait);
-
-@@ -1053,11 +1055,13 @@ void wiphy_unregister(struct wiphy *wiph
- cancel_work_sync(&rdev->conn_work);
- flush_work(&rdev->event_work);
- cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
-+ cancel_delayed_work_sync(&rdev->offchan_cac_done_wk);
- flush_work(&rdev->destroy_work);
- flush_work(&rdev->sched_scan_stop_wk);
- flush_work(&rdev->propagate_radar_detect_wk);
- flush_work(&rdev->propagate_cac_done_wk);
- flush_work(&rdev->mgmt_registrations_update_wk);
-+ flush_work(&rdev->offchan_cac_abort_wk);
-
- #ifdef CONFIG_PM
- if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -86,7 +86,8 @@ struct cfg80211_registered_device {
-
- struct wireless_dev *offchan_radar_wdev;
- struct cfg80211_chan_def offchan_radar_chandef;
-- struct delayed_work offchan_cac_work;
-+ struct delayed_work offchan_cac_done_wk;
-+ struct work_struct offchan_cac_abort_wk;
-
- /* netlink port which started critical protocol (0 means not started) */
- u32 crit_proto_nlportid;
-@@ -502,7 +503,9 @@ cfg80211_start_offchan_radar_detection(s
-
- void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev);
-
--void cfg80211_offchan_cac_work(struct work_struct *work);
-+void cfg80211_offchan_cac_done_wk(struct work_struct *work);
-+
-+void cfg80211_offchan_cac_abort_wk(struct work_struct *work);
-
- bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan);
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -971,17 +971,6 @@ void cfg80211_cac_event(struct net_devic
- }
- EXPORT_SYMBOL(cfg80211_cac_event);
-
--void cfg80211_offchan_cac_work(struct work_struct *work)
--{
-- struct delayed_work *delayed_work = to_delayed_work(work);
-- struct cfg80211_registered_device *rdev;
--
-- rdev = container_of(delayed_work, struct cfg80211_registered_device,
-- offchan_cac_work);
-- cfg80211_offchan_cac_event(&rdev->wiphy, &rdev->offchan_radar_chandef,
-- NL80211_RADAR_CAC_FINISHED);
--}
--
- static void
- __cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
-@@ -1006,7 +995,7 @@ __cfg80211_offchan_cac_event(struct cfg8
- rdev->offchan_radar_wdev = NULL;
- break;
- case NL80211_RADAR_CAC_ABORTED:
-- cancel_delayed_work(&rdev->offchan_cac_work);
-+ cancel_delayed_work(&rdev->offchan_cac_done_wk);
- wdev = rdev->offchan_radar_wdev;
- rdev->offchan_radar_wdev = NULL;
- break;
-@@ -1022,17 +1011,44 @@ __cfg80211_offchan_cac_event(struct cfg8
- nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
- }
-
--void cfg80211_offchan_cac_event(struct wiphy *wiphy,
-- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event)
-+static void
-+cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
-+{
-+ wiphy_lock(&rdev->wiphy);
-+ __cfg80211_offchan_cac_event(rdev, NULL, chandef, event);
-+ wiphy_unlock(&rdev->wiphy);
-+}
-+
-+void cfg80211_offchan_cac_done_wk(struct work_struct *work)
-+{
-+ struct delayed_work *delayed_work = to_delayed_work(work);
-+ struct cfg80211_registered_device *rdev;
-+
-+ rdev = container_of(delayed_work, struct cfg80211_registered_device,
-+ offchan_cac_done_wk);
-+ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
-+ NL80211_RADAR_CAC_FINISHED);
-+}
-+
-+void cfg80211_offchan_cac_abort_wk(struct work_struct *work)
-+{
-+ struct cfg80211_registered_device *rdev;
-+
-+ rdev = container_of(work, struct cfg80211_registered_device,
-+ offchan_cac_abort_wk);
-+ cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
-+ NL80211_RADAR_CAC_ABORTED);
-+}
-+
-+void cfg80211_offchan_cac_abort(struct wiphy *wiphy)
- {
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-
-- wiphy_lock(wiphy);
-- __cfg80211_offchan_cac_event(rdev, NULL, chandef, event);
-- wiphy_unlock(wiphy);
-+ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
- }
--EXPORT_SYMBOL(cfg80211_offchan_cac_event);
-+EXPORT_SYMBOL(cfg80211_offchan_cac_abort);
-
- int
- cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
-@@ -1062,7 +1078,7 @@ cfg80211_start_offchan_radar_detection(s
- rdev->offchan_radar_chandef = *chandef;
- __cfg80211_offchan_cac_event(rdev, wdev, chandef,
- NL80211_RADAR_CAC_STARTED);
-- queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_work,
-+ queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk,
- msecs_to_jiffies(cac_time_ms));
-
- return 0;
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Wed, 3 Nov 2021 18:02:35 +0100
-Subject: [PATCH] cfg80211: fix possible NULL pointer dereference in
- cfg80211_stop_offchan_radar_detection
-
-Fix the following NULL pointer dereference in
-cfg80211_stop_offchan_radar_detection routine that occurs when hostapd
-is stopped during the CAC on offchannel chain:
-
-Sat Jan 1 0[ 779.567851] ESR = 0x96000005
-0:12:50 2000 dae[ 779.572346] EC = 0x25: DABT (current EL), IL = 32 bits
-mon.debug hostap[ 779.578984] SET = 0, FnV = 0
-d: hostapd_inter[ 779.583445] EA = 0, S1PTW = 0
-face_deinit_free[ 779.587936] Data abort info:
-: num_bss=1 conf[ 779.592224] ISV = 0, ISS = 0x00000005
-->num_bss=1
-Sat[ 779.597403] CM = 0, WnR = 0
- Jan 1 00:12:50[ 779.601749] user pgtable: 4k pages, 39-bit VAs, pgdp=00000000418b2000
- 2000 daemon.deb[ 779.609601] [0000000000000000] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000
-ug hostapd: host[ 779.619657] Internal error: Oops: 96000005 [#1] SMP
-[ 779.770810] CPU: 0 PID: 2202 Comm: hostapd Not tainted 5.10.75 #0
-[ 779.776892] Hardware name: MediaTek MT7622 RFB1 board (DT)
-[ 779.782370] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO BTYPE=--)
-[ 779.788384] pc : cfg80211_chandef_valid+0x10/0x490 [cfg80211]
-[ 779.794128] lr : cfg80211_check_station_change+0x3190/0x3950 [cfg80211]
-[ 779.800731] sp : ffffffc01204b7e0
-[ 779.804036] x29: ffffffc01204b7e0 x28: ffffff80039bdc00
-[ 779.809340] x27: 0000000000000000 x26: ffffffc008cb3050
-[ 779.814644] x25: 0000000000000000 x24: 0000000000000002
-[ 779.819948] x23: ffffff8002630000 x22: ffffff8003e748d0
-[ 779.825252] x21: 0000000000000cc0 x20: ffffff8003da4a00
-[ 779.830556] x19: 0000000000000000 x18: ffffff8001bf7ce0
-[ 779.835860] x17: 00000000ffffffff x16: 0000000000000000
-[ 779.841164] x15: 0000000040d59200 x14: 00000000000019c0
-[ 779.846467] x13: 00000000000001c8 x12: 000636b9e9dab1c6
-[ 779.851771] x11: 0000000000000141 x10: 0000000000000820
-[ 779.857076] x9 : 0000000000000000 x8 : ffffff8003d7d038
-[ 779.862380] x7 : 0000000000000000 x6 : ffffff8003d7d038
-[ 779.867683] x5 : 0000000000000e90 x4 : 0000000000000038
-[ 779.872987] x3 : 0000000000000002 x2 : 0000000000000004
-[ 779.878291] x1 : 0000000000000000 x0 : 0000000000000000
-[ 779.883594] Call trace:
-[ 779.886039] cfg80211_chandef_valid+0x10/0x490 [cfg80211]
-[ 779.891434] cfg80211_check_station_change+0x3190/0x3950 [cfg80211]
-[ 779.897697] nl80211_radar_notify+0x138/0x19c [cfg80211]
-[ 779.903005] cfg80211_stop_offchan_radar_detection+0x7c/0x8c [cfg80211]
-[ 779.909616] __cfg80211_leave+0x2c/0x190 [cfg80211]
-[ 779.914490] cfg80211_register_netdevice+0x1c0/0x6d0 [cfg80211]
-[ 779.920404] raw_notifier_call_chain+0x50/0x70
-[ 779.924841] call_netdevice_notifiers_info+0x54/0xa0
-[ 779.929796] __dev_close_many+0x40/0x100
-[ 779.933712] __dev_change_flags+0x98/0x190
-[ 779.937800] dev_change_flags+0x20/0x60
-[ 779.941628] devinet_ioctl+0x534/0x6d0
-[ 779.945370] inet_ioctl+0x1bc/0x230
-[ 779.948849] sock_do_ioctl+0x44/0x200
-[ 779.952502] sock_ioctl+0x268/0x4c0
-[ 779.955985] __arm64_sys_ioctl+0xac/0xd0
-[ 779.959900] el0_svc_common.constprop.0+0x60/0x110
-[ 779.964682] do_el0_svc+0x1c/0x24
-[ 779.967990] el0_svc+0x10/0x1c
-[ 779.971036] el0_sync_handler+0x9c/0x120
-[ 779.974950] el0_sync+0x148/0x180
-[ 779.978259] Code: a9bc7bfd 910003fd a90153f3 aa0003f3 (f9400000)
-[ 779.984344] ---[ end trace 0e67b4f5d6cdeec7 ]---
-[ 779.996400] Kernel panic - not syncing: Oops: Fatal exception
-[ 780.002139] SMP: stopping secondary CPUs
-[ 780.006057] Kernel Offset: disabled
-[ 780.009537] CPU features: 0x0000002,04002004
-[ 780.013796] Memory Limit: none
-
-Fixes: b8f5facf286b ("cfg80211: implement APIs for dedicated radar detection HW")
-Reported-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Tested-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/c2e34c065bf8839c5ffa45498ae154021a72a520.1635958796.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -982,6 +982,9 @@ __cfg80211_offchan_cac_event(struct cfg8
-
- lockdep_assert_wiphy(&rdev->wiphy);
-
-+ if (!cfg80211_chandef_valid(chandef))
-+ return;
-+
- if (event != NL80211_RADAR_CAC_STARTED && !rdev->offchan_radar_wdev)
- return;
-
-@@ -1096,6 +1099,6 @@ void cfg80211_stop_offchan_radar_detecti
-
- rdev_set_radar_offchan(rdev, NULL);
-
-- __cfg80211_offchan_cac_event(rdev, NULL, NULL,
-+ __cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef,
- NL80211_RADAR_CAC_ABORTED);
- }
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 16 Nov 2021 12:41:52 +0100
-Subject: [PATCH] cfg80211: schedule offchan_cac_abort_wk in
- cfg80211_radar_event
-
-If necessary schedule offchan_cac_abort_wk work in cfg80211_radar_event
-routine adding offchan parameter to cfg80211_radar_event signature.
-Rename cfg80211_radar_event in __cfg80211_radar_event and introduce
-the two following inline helpers:
-- cfg80211_radar_event
-- cfg80211_offchan_radar_event
-Doing so the drv will not need to run cfg80211_offchan_cac_abort() after
-radar detection on the offchannel chain.
-
-Tested-by: Owen Peng <owen.peng@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/3ff583e021e3343a3ced54a7b09b5e184d1880dc.1637062727.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -7580,15 +7580,33 @@ void cfg80211_cqm_txe_notify(struct net_
- void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
-
- /**
-- * cfg80211_radar_event - radar detection event
-+ * __cfg80211_radar_event - radar detection event
- * @wiphy: the wiphy
- * @chandef: chandef for the current channel
-+ * @offchan: the radar has been detected on the offchannel chain
- * @gfp: context flags
- *
- * This function is called when a radar is detected on the current chanenl.
- */
--void cfg80211_radar_event(struct wiphy *wiphy,
-- struct cfg80211_chan_def *chandef, gfp_t gfp);
-+void __cfg80211_radar_event(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef,
-+ bool offchan, gfp_t gfp);
-+
-+static inline void
-+cfg80211_radar_event(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef,
-+ gfp_t gfp)
-+{
-+ __cfg80211_radar_event(wiphy, chandef, false, gfp);
-+}
-+
-+static inline void
-+cfg80211_offchan_radar_event(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef,
-+ gfp_t gfp)
-+{
-+ __cfg80211_radar_event(wiphy, chandef, true, gfp);
-+}
-
- /**
- * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -905,13 +905,13 @@ void cfg80211_dfs_channels_update_work(s
- }
-
-
--void cfg80211_radar_event(struct wiphy *wiphy,
-- struct cfg80211_chan_def *chandef,
-- gfp_t gfp)
-+void __cfg80211_radar_event(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef,
-+ bool offchan, gfp_t gfp)
- {
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-
-- trace_cfg80211_radar_event(wiphy, chandef);
-+ trace_cfg80211_radar_event(wiphy, chandef, offchan);
-
- /* only set the chandef supplied channel to unavailable, in
- * case the radar is detected on only one of multiple channels
-@@ -919,6 +919,9 @@ void cfg80211_radar_event(struct wiphy *
- */
- cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
-
-+ if (offchan)
-+ queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
-+
- cfg80211_sched_dfs_chan_update(rdev);
-
- nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
-@@ -926,7 +929,7 @@ void cfg80211_radar_event(struct wiphy *
- memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
- queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
- }
--EXPORT_SYMBOL(cfg80211_radar_event);
-+EXPORT_SYMBOL(__cfg80211_radar_event);
-
- void cfg80211_cac_event(struct net_device *netdev,
- const struct cfg80211_chan_def *chandef,
-@@ -998,7 +1001,8 @@ __cfg80211_offchan_cac_event(struct cfg8
- rdev->offchan_radar_wdev = NULL;
- break;
- case NL80211_RADAR_CAC_ABORTED:
-- cancel_delayed_work(&rdev->offchan_cac_done_wk);
-+ if (!cancel_delayed_work(&rdev->offchan_cac_done_wk))
-+ return;
- wdev = rdev->offchan_radar_wdev;
- rdev->offchan_radar_wdev = NULL;
- break;
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_n
- );
-
- TRACE_EVENT(cfg80211_radar_event,
-- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-- TP_ARGS(wiphy, chandef),
-+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
-+ bool offchan),
-+ TP_ARGS(wiphy, chandef, offchan),
- TP_STRUCT__entry(
- WIPHY_ENTRY
- CHAN_DEF_ENTRY
-+ __field(bool, offchan)
- ),
- TP_fast_assign(
- WIPHY_ASSIGN;
- CHAN_DEF_ASSIGN(chandef);
-+ __entry->offchan = offchan;
- ),
-- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
-+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d",
-+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan)
- );
-
- TRACE_EVENT(cfg80211_cac_event,
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Tue, 16 Nov 2021 15:03:36 +0100
-Subject: [PATCH] cfg80211: allow continuous radar monitoring on offchannel
- chain
-
-Allow continuous radar detection on the offchannel chain in order
-to switch to the monitored channel whenever the underlying driver
-reports a radar pattern on the main channel.
-
-Tested-by: Owen Peng <owen.peng@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/d46217310a49b14ff0e9c002f0a6e0547d70fd2c.1637071350.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -712,6 +712,19 @@ static bool cfg80211_is_wiphy_oper_chan(
- return false;
- }
-
-+static bool
-+cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
-+ struct ieee80211_channel *channel)
-+{
-+ if (!rdev->offchan_radar_wdev)
-+ return false;
-+
-+ if (!cfg80211_chandef_valid(&rdev->offchan_radar_chandef))
-+ return false;
-+
-+ return cfg80211_is_sub_chan(&rdev->offchan_radar_chandef, channel);
-+}
-+
- bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan)
- {
-@@ -728,6 +741,9 @@ bool cfg80211_any_wiphy_oper_chan(struct
-
- if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
- return true;
-+
-+ if (cfg80211_offchan_chain_is_active(rdev, chan))
-+ return true;
- }
-
- return false;
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -988,7 +988,7 @@ __cfg80211_offchan_cac_event(struct cfg8
- if (!cfg80211_chandef_valid(chandef))
- return;
-
-- if (event != NL80211_RADAR_CAC_STARTED && !rdev->offchan_radar_wdev)
-+ if (!rdev->offchan_radar_wdev)
- return;
-
- switch (event) {
-@@ -998,17 +998,13 @@ __cfg80211_offchan_cac_event(struct cfg8
- queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
- cfg80211_sched_dfs_chan_update(rdev);
- wdev = rdev->offchan_radar_wdev;
-- rdev->offchan_radar_wdev = NULL;
- break;
- case NL80211_RADAR_CAC_ABORTED:
- if (!cancel_delayed_work(&rdev->offchan_cac_done_wk))
- return;
- wdev = rdev->offchan_radar_wdev;
-- rdev->offchan_radar_wdev = NULL;
- break;
- case NL80211_RADAR_CAC_STARTED:
-- WARN_ON(!wdev);
-- rdev->offchan_radar_wdev = wdev;
- break;
- default:
- return;
-@@ -1024,7 +1020,8 @@ cfg80211_offchan_cac_event(struct cfg802
- enum nl80211_radar_event event)
- {
- wiphy_lock(&rdev->wiphy);
-- __cfg80211_offchan_cac_event(rdev, NULL, chandef, event);
-+ __cfg80211_offchan_cac_event(rdev, rdev->offchan_radar_wdev,
-+ chandef, event);
- wiphy_unlock(&rdev->wiphy);
- }
-
-@@ -1071,7 +1068,13 @@ cfg80211_start_offchan_radar_detection(s
- NL80211_EXT_FEATURE_RADAR_OFFCHAN))
- return -EOPNOTSUPP;
-
-- if (rdev->offchan_radar_wdev)
-+ /* Offchannel chain already locked by another wdev */
-+ if (rdev->offchan_radar_wdev && rdev->offchan_radar_wdev != wdev)
-+ return -EBUSY;
-+
-+ /* CAC already in progress on the offchannel chain */
-+ if (rdev->offchan_radar_wdev == wdev &&
-+ delayed_work_pending(&rdev->offchan_cac_done_wk))
- return -EBUSY;
-
- err = rdev_set_radar_offchan(rdev, chandef);
-@@ -1083,6 +1086,8 @@ cfg80211_start_offchan_radar_detection(s
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
- rdev->offchan_radar_chandef = *chandef;
-+ rdev->offchan_radar_wdev = wdev; /* Get offchain ownership */
-+
- __cfg80211_offchan_cac_event(rdev, wdev, chandef,
- NL80211_RADAR_CAC_STARTED);
- queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk,
-@@ -1102,6 +1107,7 @@ void cfg80211_stop_offchan_radar_detecti
- return;
-
- rdev_set_radar_offchan(rdev, NULL);
-+ rdev->offchan_radar_wdev = NULL; /* Release offchain ownership */
-
- __cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef,
- NL80211_RADAR_CAC_ABORTED);
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -9278,42 +9278,60 @@ static int nl80211_start_radar_detection
- struct cfg80211_chan_def chandef;
- enum nl80211_dfs_regions dfs_region;
- unsigned int cac_time_ms;
-- int err;
-+ int err = -EINVAL;
-+
-+ flush_delayed_work(&rdev->dfs_update_channels_wk);
-+
-+ wiphy_lock(wiphy);
-
- dfs_region = reg_get_dfs_region(wiphy);
- if (dfs_region == NL80211_DFS_UNSET)
-- return -EINVAL;
-+ goto unlock;
-
- err = nl80211_parse_chandef(rdev, info, &chandef);
- if (err)
-- return err;
-+ goto unlock;
-
- err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
- if (err < 0)
-- return err;
-+ goto unlock;
-
-- if (err == 0)
-- return -EINVAL;
-+ if (err == 0) {
-+ err = -EINVAL;
-+ goto unlock;
-+ }
-
-- if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
-- return -EINVAL;
-+ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) {
-+ err = -EINVAL;
-+ goto unlock;
-+ }
-
-- if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN]))
-- return cfg80211_start_offchan_radar_detection(rdev, wdev,
-- &chandef);
-+ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) {
-+ err = cfg80211_start_offchan_radar_detection(rdev, wdev,
-+ &chandef);
-+ goto unlock;
-+ }
-
-- if (netif_carrier_ok(dev))
-- return -EBUSY;
-+ if (netif_carrier_ok(dev)) {
-+ err = -EBUSY;
-+ goto unlock;
-+ }
-
-- if (wdev->cac_started)
-- return -EBUSY;
-+ if (wdev->cac_started) {
-+ err = -EBUSY;
-+ goto unlock;
-+ }
-
- /* CAC start is offloaded to HW and can't be started manually */
-- if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
-- return -EOPNOTSUPP;
-+ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) {
-+ err = -EOPNOTSUPP;
-+ goto unlock;
-+ }
-
-- if (!rdev->ops->start_radar_detection)
-- return -EOPNOTSUPP;
-+ if (!rdev->ops->start_radar_detection) {
-+ err = -EOPNOTSUPP;
-+ goto unlock;
-+ }
-
- cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
- if (WARN_ON(!cac_time_ms))
-@@ -9326,6 +9344,9 @@ static int nl80211_start_radar_detection
- wdev->cac_start_time = jiffies;
- wdev->cac_time_ms = cac_time_ms;
- }
-+unlock:
-+ wiphy_unlock(wiphy);
-+
- return err;
- }
-
-@@ -15961,7 +15982,8 @@ static const struct genl_small_ops nl802
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = nl80211_start_radar_detection,
- .flags = GENL_UNS_ADMIN_PERM,
-- .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
-+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-+ NL80211_FLAG_NO_WIPHY_MTX,
- },
- {
- .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 23 Oct 2021 11:10:51 +0200
-Subject: [PATCH] mac80211: introduce set_radar_offchan callback
-
-Similar to cfg80211, introduce set_radar_offchan callback in mac80211_ops
-in order to configure a dedicated offchannel chain available on some hw
-(e.g. mt7915) to perform offchannel CAC detection and avoid tx/rx downtime.
-
-Tested-by: Evelyn Tsai <evelyn.tsai@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/201110606d4f3a7dfdf31440e351f2e2c375d4f0.1634979655.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3937,6 +3937,14 @@ struct ieee80211_prep_tx_info {
- * twt structure.
- * @twt_teardown_request: Update the hw with TWT teardown request received
- * from the peer.
-+ * @set_radar_offchan: Configure dedicated offchannel chain available for
-+ * radar/CAC detection on some hw. This chain can't be used to transmit
-+ * or receive frames and it is bounded to a running wdev.
-+ * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * switching to a different channel during CAC detection on the selected
-+ * radar channel.
-+ * The caller is expected to set chandef pointer to NULL in order to
-+ * disable offchannel CAC/radar detection.
- * @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
- * resolve a path for hardware flow offloading
- */
-@@ -4267,6 +4275,8 @@ struct ieee80211_ops {
- struct ieee80211_twt_setup *twt);
- void (*twt_teardown_request)(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u8 flowid);
-+ int (*set_radar_offchan)(struct ieee80211_hw *hw,
-+ struct cfg80211_chan_def *chandef);
- #if LINUX_VERSION_IS_GEQ(5,10,0)
- int (*net_fill_forward_path)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4341,6 +4341,18 @@ out:
- return err;
- }
-
-+static int
-+ieee80211_set_radar_offchan(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef)
-+{
-+ struct ieee80211_local *local = wiphy_priv(wiphy);
-+
-+ if (!local->ops->set_radar_offchan)
-+ return -EOPNOTSUPP;
-+
-+ return local->ops->set_radar_offchan(&local->hw, chandef);
-+}
-+
- const struct cfg80211_ops mac80211_config_ops = {
- .add_virtual_intf = ieee80211_add_iface,
- .del_virtual_intf = ieee80211_del_iface,
-@@ -4445,4 +4457,5 @@ const struct cfg80211_ops mac80211_confi
- .reset_tid_config = ieee80211_reset_tid_config,
- .set_sar_specs = ieee80211_set_sar_specs,
- .color_change = ieee80211_color_change,
-+ .set_radar_offchan = ieee80211_set_radar_offchan,
- };
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Mon, 29 Nov 2021 14:11:24 +0100
-Subject: [PATCH] cfg80211: rename offchannel_chain structs to background_chain
- to avoid confusion with ETSI standard
-
-ETSI standard defines "Offchannel CAC" as:
-"Off-Channel CAC is performed by a number of non-continuous checks
-spread over a period in time. This period, which is required to
-determine the presence of radar signals, is defined as the Off-Channel
-CAC Time..
-Minimum Off-Channel CAC Time 6 minutes and Maximum Off-Channel CAC Time
-4 hours..".
-mac80211 implementation refers to a dedicated hw chain used for continuous
-radar monitoring. Rename offchannel_* references to background_* in
-order to avoid confusion with ETSI standard.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/4204cc1d648d76b44557981713231e030a3bd991.1638190762.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4058,14 +4058,14 @@ struct mgmt_frame_regs {
- *
- * @color_change: Initiate a color change.
- *
-- * @set_radar_offchan: Configure dedicated offchannel chain available for
-+ * @set_radar_background: Configure dedicated offchannel chain available for
- * radar/CAC detection on some hw. This chain can't be used to transmit
- * or receive frames and it is bounded to a running wdev.
-- * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * Background radar/CAC detection allows to avoid the CAC downtime
- * switching to a different channel during CAC detection on the selected
- * radar channel.
- * The caller is expected to set chandef pointer to NULL in order to
-- * disable offchannel CAC/radar detection.
-+ * disable background CAC/radar detection.
- */
- struct cfg80211_ops {
- int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
-@@ -4396,8 +4396,8 @@ struct cfg80211_ops {
- int (*color_change)(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_color_change_settings *params);
-- int (*set_radar_offchan)(struct wiphy *wiphy,
-- struct cfg80211_chan_def *chandef);
-+ int (*set_radar_background)(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef);
- };
-
- /*
-@@ -7601,9 +7601,9 @@ cfg80211_radar_event(struct wiphy *wiphy
- }
-
- static inline void
--cfg80211_offchan_radar_event(struct wiphy *wiphy,
-- struct cfg80211_chan_def *chandef,
-- gfp_t gfp)
-+cfg80211_background_radar_event(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef,
-+ gfp_t gfp)
- {
- __cfg80211_radar_event(wiphy, chandef, true, gfp);
- }
-@@ -7638,13 +7638,13 @@ void cfg80211_cac_event(struct net_devic
- enum nl80211_radar_event event, gfp_t gfp);
-
- /**
-- * cfg80211_offchan_cac_abort - Channel Availability Check offchan abort event
-+ * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
- * @wiphy: the wiphy
- *
- * This function is called by the driver when a Channel Availability Check
- * (CAC) is aborted by a offchannel dedicated chain.
- */
--void cfg80211_offchan_cac_abort(struct wiphy *wiphy);
-+void cfg80211_background_cac_abort(struct wiphy *wiphy);
-
- /**
- * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -3937,14 +3937,14 @@ struct ieee80211_prep_tx_info {
- * twt structure.
- * @twt_teardown_request: Update the hw with TWT teardown request received
- * from the peer.
-- * @set_radar_offchan: Configure dedicated offchannel chain available for
-+ * @set_radar_background: Configure dedicated offchannel chain available for
- * radar/CAC detection on some hw. This chain can't be used to transmit
- * or receive frames and it is bounded to a running wdev.
-- * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * Background radar/CAC detection allows to avoid the CAC downtime
- * switching to a different channel during CAC detection on the selected
- * radar channel.
- * The caller is expected to set chandef pointer to NULL in order to
-- * disable offchannel CAC/radar detection.
-+ * disable background CAC/radar detection.
- * @net_fill_forward_path: Called from .ndo_fill_forward_path in order to
- * resolve a path for hardware flow offloading
- */
-@@ -4275,8 +4275,8 @@ struct ieee80211_ops {
- struct ieee80211_twt_setup *twt);
- void (*twt_teardown_request)(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta, u8 flowid);
-- int (*set_radar_offchan)(struct ieee80211_hw *hw,
-- struct cfg80211_chan_def *chandef);
-+ int (*set_radar_background)(struct ieee80211_hw *hw,
-+ struct cfg80211_chan_def *chandef);
- #if LINUX_VERSION_IS_GEQ(5,10,0)
- int (*net_fill_forward_path)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -2608,10 +2608,10 @@ enum nl80211_commands {
- * Mandatory parameter for the transmitting interface to enable MBSSID.
- * Optional for the non-transmitting interfaces.
- *
-- * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for
-- * radar/CAC detection on some hw. This chain can't be used to transmit
-- * or receive frames and it is bounded to a running wdev.
-- * Offchannel radar/CAC detection allows to avoid the CAC downtime
-+ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain
-+ * available for radar/CAC detection on some hw. This chain can't be used
-+ * to transmit or receive frames and it is bounded to a running wdev.
-+ * Background radar/CAC detection allows to avoid the CAC downtime
- * switching on a different channel during CAC detection on the selected
- * radar channel.
- *
-@@ -3121,7 +3121,7 @@ enum nl80211_attrs {
- NL80211_ATTR_MBSSID_CONFIG,
- NL80211_ATTR_MBSSID_ELEMS,
-
-- NL80211_ATTR_RADAR_OFFCHAN,
-+ NL80211_ATTR_RADAR_BACKGROUND,
-
- /* add attributes here, update the policy in nl80211.c */
-
-@@ -6022,7 +6022,7 @@ enum nl80211_feature_flags {
- * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
- * detection and change announcemnts.
- *
-- * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
-+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
- * detection.
- *
- * @NUM_NL80211_EXT_FEATURES: number of extended features.
-@@ -6090,7 +6090,7 @@ enum nl80211_ext_feature_index {
- NL80211_EXT_FEATURE_SECURE_RTT,
- NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
- NL80211_EXT_FEATURE_BSS_COLOR,
-- NL80211_EXT_FEATURE_RADAR_OFFCHAN,
-+ NL80211_EXT_FEATURE_RADAR_BACKGROUND,
-
- /* add new features before the definition below */
- NUM_NL80211_EXT_FEATURES,
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4342,15 +4342,15 @@ out:
- }
-
- static int
--ieee80211_set_radar_offchan(struct wiphy *wiphy,
-- struct cfg80211_chan_def *chandef)
-+ieee80211_set_radar_background(struct wiphy *wiphy,
-+ struct cfg80211_chan_def *chandef)
- {
- struct ieee80211_local *local = wiphy_priv(wiphy);
-
-- if (!local->ops->set_radar_offchan)
-+ if (!local->ops->set_radar_background)
- return -EOPNOTSUPP;
-
-- return local->ops->set_radar_offchan(&local->hw, chandef);
-+ return local->ops->set_radar_background(&local->hw, chandef);
- }
-
- const struct cfg80211_ops mac80211_config_ops = {
-@@ -4457,5 +4457,5 @@ const struct cfg80211_ops mac80211_confi
- .reset_tid_config = ieee80211_reset_tid_config,
- .set_sar_specs = ieee80211_set_sar_specs,
- .color_change = ieee80211_color_change,
-- .set_radar_offchan = ieee80211_set_radar_offchan,
-+ .set_radar_background = ieee80211_set_radar_background,
- };
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -716,13 +716,13 @@ static bool
- cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
- struct ieee80211_channel *channel)
- {
-- if (!rdev->offchan_radar_wdev)
-+ if (!rdev->background_radar_wdev)
- return false;
-
-- if (!cfg80211_chandef_valid(&rdev->offchan_radar_chandef))
-+ if (!cfg80211_chandef_valid(&rdev->background_radar_chandef))
- return false;
-
-- return cfg80211_is_sub_chan(&rdev->offchan_radar_chandef, channel);
-+ return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel);
- }
-
- bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -543,9 +543,10 @@ use_default_name:
- INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
- INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
- INIT_WORK(&rdev->event_work, cfg80211_event_work);
-- INIT_WORK(&rdev->offchan_cac_abort_wk, cfg80211_offchan_cac_abort_wk);
-- INIT_DELAYED_WORK(&rdev->offchan_cac_done_wk,
-- cfg80211_offchan_cac_done_wk);
-+ INIT_WORK(&rdev->background_cac_abort_wk,
-+ cfg80211_background_cac_abort_wk);
-+ INIT_DELAYED_WORK(&rdev->background_cac_done_wk,
-+ cfg80211_background_cac_done_wk);
-
- init_waitqueue_head(&rdev->dev_wait);
-
-@@ -1055,13 +1056,13 @@ void wiphy_unregister(struct wiphy *wiph
- cancel_work_sync(&rdev->conn_work);
- flush_work(&rdev->event_work);
- cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
-- cancel_delayed_work_sync(&rdev->offchan_cac_done_wk);
-+ cancel_delayed_work_sync(&rdev->background_cac_done_wk);
- flush_work(&rdev->destroy_work);
- flush_work(&rdev->sched_scan_stop_wk);
- flush_work(&rdev->propagate_radar_detect_wk);
- flush_work(&rdev->propagate_cac_done_wk);
- flush_work(&rdev->mgmt_registrations_update_wk);
-- flush_work(&rdev->offchan_cac_abort_wk);
-+ flush_work(&rdev->background_cac_abort_wk);
-
- #ifdef CONFIG_PM
- if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
-@@ -1210,7 +1211,7 @@ void __cfg80211_leave(struct cfg80211_re
-
- cfg80211_pmsr_wdev_down(wdev);
-
-- cfg80211_stop_offchan_radar_detection(wdev);
-+ cfg80211_stop_background_radar_detection(wdev);
-
- switch (wdev->iftype) {
- case NL80211_IFTYPE_ADHOC:
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -84,10 +84,10 @@ struct cfg80211_registered_device {
-
- struct delayed_work dfs_update_channels_wk;
-
-- struct wireless_dev *offchan_radar_wdev;
-- struct cfg80211_chan_def offchan_radar_chandef;
-- struct delayed_work offchan_cac_done_wk;
-- struct work_struct offchan_cac_abort_wk;
-+ struct wireless_dev *background_radar_wdev;
-+ struct cfg80211_chan_def background_radar_chandef;
-+ struct delayed_work background_cac_done_wk;
-+ struct work_struct background_cac_abort_wk;
-
- /* netlink port which started critical protocol (0 means not started) */
- u32 crit_proto_nlportid;
-@@ -497,15 +497,15 @@ cfg80211_chandef_dfs_cac_time(struct wip
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
- int
--cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
-- struct wireless_dev *wdev,
-- struct cfg80211_chan_def *chandef);
-+cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ struct cfg80211_chan_def *chandef);
-
--void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev);
-+void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev);
-
--void cfg80211_offchan_cac_done_wk(struct work_struct *work);
-+void cfg80211_background_cac_done_wk(struct work_struct *work);
-
--void cfg80211_offchan_cac_abort_wk(struct work_struct *work);
-+void cfg80211_background_cac_abort_wk(struct work_struct *work);
-
- bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan);
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -920,7 +920,7 @@ void __cfg80211_radar_event(struct wiphy
- cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
-
- if (offchan)
-- queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
-+ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk);
-
- cfg80211_sched_dfs_chan_update(rdev);
-
-@@ -975,10 +975,10 @@ void cfg80211_cac_event(struct net_devic
- EXPORT_SYMBOL(cfg80211_cac_event);
-
- static void
--__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
-- struct wireless_dev *wdev,
-- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event)
-+__cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
- {
- struct wiphy *wiphy = &rdev->wiphy;
- struct net_device *netdev;
-@@ -988,7 +988,7 @@ __cfg80211_offchan_cac_event(struct cfg8
- if (!cfg80211_chandef_valid(chandef))
- return;
-
-- if (!rdev->offchan_radar_wdev)
-+ if (!rdev->background_radar_wdev)
- return;
-
- switch (event) {
-@@ -997,12 +997,12 @@ __cfg80211_offchan_cac_event(struct cfg8
- memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
- queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
- cfg80211_sched_dfs_chan_update(rdev);
-- wdev = rdev->offchan_radar_wdev;
-+ wdev = rdev->background_radar_wdev;
- break;
- case NL80211_RADAR_CAC_ABORTED:
-- if (!cancel_delayed_work(&rdev->offchan_cac_done_wk))
-+ if (!cancel_delayed_work(&rdev->background_cac_done_wk))
- return;
-- wdev = rdev->offchan_radar_wdev;
-+ wdev = rdev->background_radar_wdev;
- break;
- case NL80211_RADAR_CAC_STARTED:
- break;
-@@ -1015,49 +1015,49 @@ __cfg80211_offchan_cac_event(struct cfg8
- }
-
- static void
--cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
-- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event)
-+cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
-+ const struct cfg80211_chan_def *chandef,
-+ enum nl80211_radar_event event)
- {
- wiphy_lock(&rdev->wiphy);
-- __cfg80211_offchan_cac_event(rdev, rdev->offchan_radar_wdev,
-- chandef, event);
-+ __cfg80211_background_cac_event(rdev, rdev->background_radar_wdev,
-+ chandef, event);
- wiphy_unlock(&rdev->wiphy);
- }
-
--void cfg80211_offchan_cac_done_wk(struct work_struct *work)
-+void cfg80211_background_cac_done_wk(struct work_struct *work)
- {
- struct delayed_work *delayed_work = to_delayed_work(work);
- struct cfg80211_registered_device *rdev;
-
- rdev = container_of(delayed_work, struct cfg80211_registered_device,
-- offchan_cac_done_wk);
-- cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
-- NL80211_RADAR_CAC_FINISHED);
-+ background_cac_done_wk);
-+ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef,
-+ NL80211_RADAR_CAC_FINISHED);
- }
-
--void cfg80211_offchan_cac_abort_wk(struct work_struct *work)
-+void cfg80211_background_cac_abort_wk(struct work_struct *work)
- {
- struct cfg80211_registered_device *rdev;
-
- rdev = container_of(work, struct cfg80211_registered_device,
-- offchan_cac_abort_wk);
-- cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
-- NL80211_RADAR_CAC_ABORTED);
-+ background_cac_abort_wk);
-+ cfg80211_background_cac_event(rdev, &rdev->background_radar_chandef,
-+ NL80211_RADAR_CAC_ABORTED);
- }
-
--void cfg80211_offchan_cac_abort(struct wiphy *wiphy)
-+void cfg80211_background_cac_abort(struct wiphy *wiphy)
- {
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-
-- queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
-+ queue_work(cfg80211_wq, &rdev->background_cac_abort_wk);
- }
--EXPORT_SYMBOL(cfg80211_offchan_cac_abort);
-+EXPORT_SYMBOL(cfg80211_background_cac_abort);
-
- int
--cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
-- struct wireless_dev *wdev,
-- struct cfg80211_chan_def *chandef)
-+cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev,
-+ struct wireless_dev *wdev,
-+ struct cfg80211_chan_def *chandef)
- {
- unsigned int cac_time_ms;
- int err;
-@@ -1065,19 +1065,19 @@ cfg80211_start_offchan_radar_detection(s
- lockdep_assert_wiphy(&rdev->wiphy);
-
- if (!wiphy_ext_feature_isset(&rdev->wiphy,
-- NL80211_EXT_FEATURE_RADAR_OFFCHAN))
-+ NL80211_EXT_FEATURE_RADAR_BACKGROUND))
- return -EOPNOTSUPP;
-
- /* Offchannel chain already locked by another wdev */
-- if (rdev->offchan_radar_wdev && rdev->offchan_radar_wdev != wdev)
-+ if (rdev->background_radar_wdev && rdev->background_radar_wdev != wdev)
- return -EBUSY;
-
- /* CAC already in progress on the offchannel chain */
-- if (rdev->offchan_radar_wdev == wdev &&
-- delayed_work_pending(&rdev->offchan_cac_done_wk))
-+ if (rdev->background_radar_wdev == wdev &&
-+ delayed_work_pending(&rdev->background_cac_done_wk))
- return -EBUSY;
-
-- err = rdev_set_radar_offchan(rdev, chandef);
-+ err = rdev_set_radar_background(rdev, chandef);
- if (err)
- return err;
-
-@@ -1085,30 +1085,31 @@ cfg80211_start_offchan_radar_detection(s
- if (!cac_time_ms)
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
-- rdev->offchan_radar_chandef = *chandef;
-- rdev->offchan_radar_wdev = wdev; /* Get offchain ownership */
-+ rdev->background_radar_chandef = *chandef;
-+ rdev->background_radar_wdev = wdev; /* Get offchain ownership */
-
-- __cfg80211_offchan_cac_event(rdev, wdev, chandef,
-- NL80211_RADAR_CAC_STARTED);
-- queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk,
-+ __cfg80211_background_cac_event(rdev, wdev, chandef,
-+ NL80211_RADAR_CAC_STARTED);
-+ queue_delayed_work(cfg80211_wq, &rdev->background_cac_done_wk,
- msecs_to_jiffies(cac_time_ms));
-
- return 0;
- }
-
--void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev)
-+void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev)
- {
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-
- lockdep_assert_wiphy(wiphy);
-
-- if (wdev != rdev->offchan_radar_wdev)
-+ if (wdev != rdev->background_radar_wdev)
- return;
-
-- rdev_set_radar_offchan(rdev, NULL);
-- rdev->offchan_radar_wdev = NULL; /* Release offchain ownership */
-+ rdev_set_radar_background(rdev, NULL);
-+ rdev->background_radar_wdev = NULL; /* Release offchain ownership */
-
-- __cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef,
-- NL80211_RADAR_CAC_ABORTED);
-+ __cfg80211_background_cac_event(rdev, wdev,
-+ &rdev->background_radar_chandef,
-+ NL80211_RADAR_CAC_ABORTED);
- }
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -801,7 +801,7 @@ static const struct nla_policy nl80211_p
- [NL80211_ATTR_MBSSID_CONFIG] =
- NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
- [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
-- [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG },
-+ [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
- };
-
- /* policy for the key attributes */
-@@ -9306,9 +9306,9 @@ static int nl80211_start_radar_detection
- goto unlock;
- }
-
-- if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) {
-- err = cfg80211_start_offchan_radar_detection(rdev, wdev,
-- &chandef);
-+ if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_BACKGROUND])) {
-+ err = cfg80211_start_background_radar_detection(rdev, wdev,
-+ &chandef);
- goto unlock;
- }
-
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1382,17 +1382,17 @@ static inline int rdev_color_change(stru
- }
-
- static inline int
--rdev_set_radar_offchan(struct cfg80211_registered_device *rdev,
-- struct cfg80211_chan_def *chandef)
-+rdev_set_radar_background(struct cfg80211_registered_device *rdev,
-+ struct cfg80211_chan_def *chandef)
- {
- struct wiphy *wiphy = &rdev->wiphy;
- int ret;
-
-- if (!rdev->ops->set_radar_offchan)
-+ if (!rdev->ops->set_radar_background)
- return -EOPNOTSUPP;
-
-- trace_rdev_set_radar_offchan(wiphy, chandef);
-- ret = rdev->ops->set_radar_offchan(wiphy, chandef);
-+ trace_rdev_set_radar_background(wiphy, chandef);
-+ ret = rdev->ops->set_radar_background(wiphy, chandef);
- trace_rdev_return_int(wiphy, ret);
-
- return ret;
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -3646,7 +3646,7 @@ TRACE_EVENT(cfg80211_bss_color_notify,
- __entry->color_bitmap)
- );
-
--TRACE_EVENT(rdev_set_radar_offchan,
-+TRACE_EVENT(rdev_set_radar_background,
- TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-
- TP_ARGS(wiphy, chandef),
+++ /dev/null
-From: John Crispin <john@phrozen.org>
-Date: Wed, 15 Sep 2021 19:54:35 -0700
-Subject: [PATCH] mac80211: MBSSID support in interface handling
-
-Configure multiple BSSID and enhanced multi-BSSID advertisement (EMA)
-parameters in mac80211 for AP mode.
-
-For each interface, 'mbssid_tx_vif' points to the transmitting interface of
-the MBSSID set. The pointer is set to NULL if MBSSID is disabled.
-
-Function ieee80211_stop() is modified to always bring down all the
-non-transmitting interfaces first and the transmitting interface last.
-
-Signed-off-by: John Crispin <john@phrozen.org>
-Co-developed-by: Aloka Dixit <alokad@codeaurora.org>
-Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
-Link: https://lore.kernel.org/r/20210916025437.29138-3-alokad@codeaurora.org
-[slightly change logic to be more obvious]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1719,6 +1719,7 @@ enum ieee80211_offload_flags {
- * write-protected by sdata_lock and local->mtx so holding either is fine
- * for read access.
- * @color_change_color: the bss color that will be used after the change.
-+ * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled.
- */
- struct ieee80211_vif {
- enum nl80211_iftype type;
-@@ -1750,6 +1751,8 @@ struct ieee80211_vif {
- bool color_change_active;
- u8 color_change_color;
-
-+ struct ieee80211_vif *mbssid_tx_vif;
-+
- /* must be last */
- u8 drv_priv[] __aligned(sizeof(void *));
- };
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -112,6 +112,36 @@ static int ieee80211_set_mon_options(str
- return 0;
- }
-
-+static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
-+ struct cfg80211_mbssid_config params)
-+{
-+ struct ieee80211_sub_if_data *tx_sdata;
-+
-+ sdata->vif.mbssid_tx_vif = NULL;
-+ sdata->vif.bss_conf.bssid_index = 0;
-+ sdata->vif.bss_conf.nontransmitted = false;
-+ sdata->vif.bss_conf.ema_ap = false;
-+
-+ if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev)
-+ return -EINVAL;
-+
-+ tx_sdata = IEEE80211_WDEV_TO_SUB_IF(params.tx_wdev);
-+ if (!tx_sdata)
-+ return -EINVAL;
-+
-+ if (tx_sdata == sdata) {
-+ sdata->vif.mbssid_tx_vif = &sdata->vif;
-+ } else {
-+ sdata->vif.mbssid_tx_vif = &tx_sdata->vif;
-+ sdata->vif.bss_conf.nontransmitted = true;
-+ sdata->vif.bss_conf.bssid_index = params.index;
-+ }
-+ if (params.ema)
-+ sdata->vif.bss_conf.ema_ap = true;
-+
-+ return 0;
-+}
-+
- static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
- const char *name,
- unsigned char name_assign_type,
-@@ -1107,6 +1137,14 @@ static int ieee80211_start_ap(struct wip
- changed |= BSS_CHANGED_HE_BSS_COLOR;
- }
-
-+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
-+ params->mbssid_config.tx_wdev) {
-+ err = ieee80211_set_ap_mbssid_options(sdata,
-+ params->mbssid_config);
-+ if (err)
-+ return err;
-+ }
-+
- mutex_lock(&local->mtx);
- err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
- IEEE80211_CHANCTX_SHARED);
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -632,17 +632,46 @@ static void ieee80211_do_stop(struct iee
- ieee80211_add_virtual_monitor(local);
- }
-
-+static void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
-+{
-+ struct ieee80211_sub_if_data *tx_sdata, *non_tx_sdata, *tmp_sdata;
-+ struct ieee80211_vif *tx_vif = sdata->vif.mbssid_tx_vif;
-+
-+ if (!tx_vif)
-+ return;
-+
-+ tx_sdata = vif_to_sdata(tx_vif);
-+ sdata->vif.mbssid_tx_vif = NULL;
-+
-+ list_for_each_entry_safe(non_tx_sdata, tmp_sdata,
-+ &tx_sdata->local->interfaces, list) {
-+ if (non_tx_sdata != sdata && non_tx_sdata != tx_sdata &&
-+ non_tx_sdata->vif.mbssid_tx_vif == tx_vif &&
-+ ieee80211_sdata_running(non_tx_sdata)) {
-+ non_tx_sdata->vif.mbssid_tx_vif = NULL;
-+ dev_close(non_tx_sdata->wdev.netdev);
-+ }
-+ }
-+
-+ if (sdata != tx_sdata && ieee80211_sdata_running(tx_sdata)) {
-+ tx_sdata->vif.mbssid_tx_vif = NULL;
-+ dev_close(tx_sdata->wdev.netdev);
-+ }
-+}
-+
- static int ieee80211_stop(struct net_device *dev)
- {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-- /* close all dependent VLAN interfaces before locking wiphy */
-+ /* close dependent VLAN and MBSSID interfaces before locking wiphy */
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- struct ieee80211_sub_if_data *vlan, *tmpsdata;
-
- list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
- u.vlan.list)
- dev_close(vlan->dev);
-+
-+ ieee80211_stop_mbssid(sdata);
- }
-
- wiphy_lock(sdata->local->hw.wiphy);
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Feb 2022 12:54:58 +0100
-Subject: [PATCH] mac80211: MBSSID beacon handling in AP mode
-
-Add new fields in struct beacon_data to store all MBSSID elements.
-Generate a beacon template which includes all MBSSID elements.
-Move CSA offset to reflect the MBSSID element length.
-
-Co-developed-by: Aloka Dixit <alokad@codeaurora.org>
-Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
-Co-developed-by: John Crispin <john@phrozen.org>
-Signed-off-by: John Crispin <john@phrozen.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Tested-by: Money Wang <money.wang@mediatek.com>
-Link: https://lore.kernel.org/r/5322db3c303f431adaf191ab31c45e151dde5465.1645702516.git.lorenzo@kernel.org
-[small cleanups]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -4938,12 +4938,14 @@ void ieee80211_report_low_ack(struct iee
- * @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
- * to countdown counters. This array can contain zero values which
- * should be ignored.
-+ * @mbssid_off: position of the multiple bssid element
- */
- struct ieee80211_mutable_offsets {
- u16 tim_offset;
- u16 tim_length;
-
- u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
-+ u16 mbssid_off;
- };
-
- /**
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -989,11 +989,29 @@ static int ieee80211_set_ftm_responder_p
- return 0;
- }
-
-+static int
-+ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst,
-+ struct cfg80211_mbssid_elems *src)
-+{
-+ int i, offset = 0;
-+
-+ for (i = 0; i < src->cnt; i++) {
-+ memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
-+ dst->elem[i].len = src->elem[i].len;
-+ dst->elem[i].data = pos + offset;
-+ offset += dst->elem[i].len;
-+ }
-+ dst->cnt = src->cnt;
-+
-+ return offset;
-+}
-+
- static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_beacon_data *params,
- const struct ieee80211_csa_settings *csa,
- const struct ieee80211_color_change_settings *cca)
- {
-+ struct cfg80211_mbssid_elems *mbssid = NULL;
- struct beacon_data *new, *old;
- int new_head_len, new_tail_len;
- int size, err;
-@@ -1021,6 +1039,17 @@ static int ieee80211_assign_beacon(struc
-
- size = sizeof(*new) + new_head_len + new_tail_len;
-
-+ /* new or old multiple BSSID elements? */
-+ if (params->mbssid_ies) {
-+ mbssid = params->mbssid_ies;
-+ size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-+ size += ieee80211_get_mbssid_beacon_len(mbssid);
-+ } else if (old && old->mbssid_ies) {
-+ mbssid = old->mbssid_ies;
-+ size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-+ size += ieee80211_get_mbssid_beacon_len(mbssid);
-+ }
-+
- new = kzalloc(size, GFP_KERNEL);
- if (!new)
- return -ENOMEM;
-@@ -1029,12 +1058,20 @@ static int ieee80211_assign_beacon(struc
-
- /*
- * pointers go into the block we allocated,
-- * memory is | beacon_data | head | tail |
-+ * memory is | beacon_data | head | tail | mbssid_ies
- */
- new->head = ((u8 *) new) + sizeof(*new);
- new->tail = new->head + new_head_len;
- new->head_len = new_head_len;
- new->tail_len = new_tail_len;
-+ /* copy in optional mbssid_ies */
-+ if (mbssid) {
-+ u8 *pos = new->tail + new->tail_len;
-+
-+ new->mbssid_ies = (void *)pos;
-+ pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
-+ ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid);
-+ }
-
- if (csa) {
- new->cntdwn_current_counter = csa->count;
-@@ -1332,8 +1369,11 @@ static int ieee80211_stop_ap(struct wiph
-
- mutex_unlock(&local->mtx);
-
-- kfree(sdata->u.ap.next_beacon);
-- sdata->u.ap.next_beacon = NULL;
-+ if (sdata->u.ap.next_beacon) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
-+ }
-
- /* turn off carrier for this interface and dependent VLANs */
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-@@ -3126,12 +3166,24 @@ cfg80211_beacon_dup(struct cfg80211_beac
-
- len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
- beacon->proberesp_ies_len + beacon->assocresp_ies_len +
-- beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
-+ beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len +
-+ ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
-
- new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
- if (!new_beacon)
- return NULL;
-
-+ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
-+ new_beacon->mbssid_ies =
-+ kzalloc(struct_size(new_beacon->mbssid_ies,
-+ elem, beacon->mbssid_ies->cnt),
-+ GFP_KERNEL);
-+ if (!new_beacon->mbssid_ies) {
-+ kfree(new_beacon);
-+ return NULL;
-+ }
-+ }
-+
- pos = (u8 *)(new_beacon + 1);
- if (beacon->head_len) {
- new_beacon->head_len = beacon->head_len;
-@@ -3169,6 +3221,10 @@ cfg80211_beacon_dup(struct cfg80211_beac
- memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
- pos += beacon->probe_resp_len;
- }
-+ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt)
-+ pos += ieee80211_copy_mbssid_beacon(pos,
-+ new_beacon->mbssid_ies,
-+ beacon->mbssid_ies);
-
- /* might copy -1, meaning no changes requested */
- new_beacon->ftm_responder = beacon->ftm_responder;
-@@ -3206,8 +3262,11 @@ static int ieee80211_set_after_csa_beaco
- case NL80211_IFTYPE_AP:
- err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
- NULL, NULL);
-- kfree(sdata->u.ap.next_beacon);
-- sdata->u.ap.next_beacon = NULL;
-+ if (sdata->u.ap.next_beacon) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
-+ }
-
- if (err < 0)
- return err;
-@@ -3362,8 +3421,12 @@ static int ieee80211_set_csa_beacon(stru
- if ((params->n_counter_offsets_beacon >
- IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
- (params->n_counter_offsets_presp >
-- IEEE80211_MAX_CNTDWN_COUNTERS_NUM))
-+ IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
- return -EINVAL;
-+ }
-
- csa.counter_offsets_beacon = params->counter_offsets_beacon;
- csa.counter_offsets_presp = params->counter_offsets_presp;
-@@ -3373,7 +3436,9 @@ static int ieee80211_set_csa_beacon(stru
-
- err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL);
- if (err < 0) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
- kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
- return err;
- }
- *changed |= err;
-@@ -3460,8 +3525,11 @@ static int ieee80211_set_csa_beacon(stru
- static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata)
- {
- sdata->vif.color_change_active = false;
-- kfree(sdata->u.ap.next_beacon);
-- sdata->u.ap.next_beacon = NULL;
-+ if (sdata->u.ap.next_beacon) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
-+ }
-
- cfg80211_color_change_aborted_notify(sdata->dev);
- }
-@@ -4199,8 +4267,11 @@ ieee80211_set_after_color_change_beacon(
-
- ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
- NULL, NULL);
-- kfree(sdata->u.ap.next_beacon);
-- sdata->u.ap.next_beacon = NULL;
-+ if (sdata->u.ap.next_beacon) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
-+ }
-
- if (ret < 0)
- return ret;
-@@ -4243,7 +4314,11 @@ ieee80211_set_color_change_beacon(struct
- err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change,
- NULL, &color_change);
- if (err < 0) {
-- kfree(sdata->u.ap.next_beacon);
-+ if (sdata->u.ap.next_beacon) {
-+ kfree(sdata->u.ap.next_beacon->mbssid_ies);
-+ kfree(sdata->u.ap.next_beacon);
-+ sdata->u.ap.next_beacon = NULL;
-+ }
- return err;
- }
- *changed |= err;
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -261,6 +261,7 @@ struct beacon_data {
- struct ieee80211_meshconf_ie *meshconf;
- u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
- u8 cntdwn_current_counter;
-+ struct cfg80211_mbssid_elems *mbssid_ies;
- struct rcu_head rcu_head;
- };
-
-@@ -1082,6 +1083,20 @@ ieee80211_vif_get_shift(struct ieee80211
- return shift;
- }
-
-+static inline int
-+ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems)
-+{
-+ int i, len = 0;
-+
-+ if (!elems)
-+ return 0;
-+
-+ for (i = 0; i < elems->cnt; i++)
-+ len += elems->elem[i].len;
-+
-+ return len;
-+}
-+
- enum {
- IEEE80211_RX_MSG = 1,
- IEEE80211_TX_STATUS_MSG = 2,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -5041,6 +5041,19 @@ ieee80211_beacon_get_finish(struct ieee8
- IEEE80211_TX_CTL_FIRST_FRAGMENT;
- }
-
-+static void
-+ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon)
-+{
-+ int i;
-+
-+ if (!beacon->mbssid_ies)
-+ return;
-+
-+ for (i = 0; i < beacon->mbssid_ies->cnt; i++)
-+ skb_put_data(skb, beacon->mbssid_ies->elem[i].data,
-+ beacon->mbssid_ies->elem[i].len);
-+}
-+
- static struct sk_buff *
- ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
-@@ -5054,6 +5067,7 @@ ieee80211_beacon_get_ap(struct ieee80211
- struct ieee80211_if_ap *ap = &sdata->u.ap;
- struct sk_buff *skb = NULL;
- u16 csa_off_base = 0;
-+ int mbssid_len;
-
- if (beacon->cntdwn_counter_offsets[0]) {
- if (!is_template)
-@@ -5063,11 +5077,12 @@ ieee80211_beacon_get_ap(struct ieee80211
- }
-
- /* headroom, head length,
-- * tail length and maximum TIM length
-+ * tail length, maximum TIM length and multiple BSSID length
- */
-+ mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies);
- skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
- beacon->tail_len + 256 +
-- local->hw.extra_beacon_tailroom);
-+ local->hw.extra_beacon_tailroom + mbssid_len);
- if (!skb)
- return NULL;
-
-@@ -5081,6 +5096,11 @@ ieee80211_beacon_get_ap(struct ieee80211
- offs->tim_length = skb->len - beacon->head_len;
- offs->cntdwn_counter_offs[0] = beacon->cntdwn_counter_offsets[0];
-
-+ if (mbssid_len) {
-+ ieee80211_beacon_add_mbssid(skb, beacon);
-+ offs->mbssid_off = skb->len - mbssid_len;
-+ }
-+
- /* for AP the csa offsets are from tail */
- csa_off_base = skb->len;
- }
+++ /dev/null
-From: John Crispin <john@phrozen.org>
-Date: Thu, 24 Feb 2022 12:54:59 +0100
-Subject: [PATCH] mac80211: MBSSID channel switch
-
-Trigger ieee80211_csa_finish() on the non-transmitting interfaces
-when channel switch concludes on the transmitting interface.
-
-Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Co-developed-by: Aloka Dixit <alokad@codeaurora.org>
-Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
-Signed-off-by: John Crispin <john@phrozen.org>
-Link: https://lore.kernel.org/r/6fde4d7f9fa387494f46a7aa4a584478dcda06f1.1645702516.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3247,9 +3247,31 @@ cfg80211_beacon_dup(struct cfg80211_beac
- void ieee80211_csa_finish(struct ieee80211_vif *vif)
- {
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-+ struct ieee80211_local *local = sdata->local;
-
-- ieee80211_queue_work(&sdata->local->hw,
-- &sdata->csa_finalize_work);
-+ rcu_read_lock();
-+
-+ if (vif->mbssid_tx_vif == vif) {
-+ /* Trigger ieee80211_csa_finish() on the non-transmitting
-+ * interfaces when channel switch is received on
-+ * transmitting interface
-+ */
-+ struct ieee80211_sub_if_data *iter;
-+
-+ list_for_each_entry_rcu(iter, &local->interfaces, list) {
-+ if (!ieee80211_sdata_running(iter))
-+ continue;
-+
-+ if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
-+ continue;
-+
-+ ieee80211_queue_work(&iter->local->hw,
-+ &iter->csa_finalize_work);
-+ }
-+ }
-+ ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work);
-+
-+ rcu_read_unlock();
- }
- EXPORT_SYMBOL(ieee80211_csa_finish);
-
+++ /dev/null
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 24 Feb 2022 12:55:00 +0100
-Subject: [PATCH] mac80211: update bssid_indicator in
- ieee80211_assign_beacon
-
-Update bssid_indicator in ieee80211_bss_conf according to the
-number of bssid in the set.
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/f92317e002fca9933f05a445fcefb4f53291d601.1645702516.git.lorenzo@kernel.org
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1071,6 +1071,9 @@ static int ieee80211_assign_beacon(struc
- new->mbssid_ies = (void *)pos;
- pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
- ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid);
-+ /* update bssid_indicator */
-+ sdata->vif.bss_conf.bssid_indicator =
-+ ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
- }
-
- if (csa) {
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 26 Mar 2022 23:58:35 +0100
-Subject: [PATCH] mac80211: do not wake queues on a vif that is being stopped
-
-When a vif is being removed and sdata->bss is cleared, __ieee80211_wake_txqs
-can still be called on it, which crashes as soon as sdata->bss is being
-dereferenced.
-To fix this properly, check for SDATA_STATE_RUNNING before waking queues,
-and take the fq lock when setting it (to ensure that __ieee80211_wake_txqs
-observes the change when running on a different CPU
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -377,7 +377,9 @@ static void ieee80211_do_stop(struct iee
- bool cancel_scan;
- struct cfg80211_nan_func *func;
-
-+ spin_lock_bh(&local->fq.lock);
- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
-+ spin_unlock_bh(&local->fq.lock);
-
- cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
- if (cancel_scan)
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -301,6 +301,9 @@ static void __ieee80211_wake_txqs(struct
- local_bh_disable();
- spin_lock(&fq->lock);
-
-+ if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
-+ goto out;
-+
- if (sdata->vif.type == NL80211_IFTYPE_AP)
- ps = &sdata->bss->ps;
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 19 Jun 2022 23:13:05 +0200
-Subject: [PATCH] mac80211: switch airtime fairness back to deficit round-robin
- scheduling
-
-This reverts commits 6a789ba679d652587532cec2a0e0274fda172f3b and
-2433647bc8d983a543e7d31b41ca2de1c7e2c198.
-
-The virtual time scheduler code has a number of issues:
-- queues slowed down by hardware/firmware powersave handling were not properly
- handled.
-- on ath10k in push-pull mode, tx queues that the driver tries to pull from
- were starved, causing excessive latency
-- delay between tx enqueue and reported airtime use were causing excessively
- bursty tx behavior
-
-The bursty behavior may also be present on the round-robin scheduler, but there
-it is much easier to fix without introducing additional regressions
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -6666,6 +6666,9 @@ static inline void ieee80211_txq_schedul
- {
- }
-
-+void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq, bool force);
-+
- /**
- * ieee80211_schedule_txq - schedule a TXQ for transmission
- *
-@@ -6678,7 +6681,11 @@ static inline void ieee80211_txq_schedul
- * The driver may call this function if it has buffered packets for
- * this TXQ internally.
- */
--void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
-+static inline void
-+ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
-+{
-+ __ieee80211_schedule_txq(hw, txq, true);
-+}
-
- /**
- * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
-@@ -6690,8 +6697,12 @@ void ieee80211_schedule_txq(struct ieee8
- * The driver may set force=true if it has buffered packets for this TXQ
- * internally.
- */
--void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
-- bool force);
-+static inline void
-+ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
-+ bool force)
-+{
-+ __ieee80211_schedule_txq(hw, txq, force);
-+}
-
- /**
- * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1554,38 +1554,6 @@ static void sta_apply_mesh_params(struct
- #endif
- }
-
--static void sta_apply_airtime_params(struct ieee80211_local *local,
-- struct sta_info *sta,
-- struct station_parameters *params)
--{
-- u8 ac;
--
-- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- struct airtime_sched_info *air_sched = &local->airtime[ac];
-- struct airtime_info *air_info = &sta->airtime[ac];
-- struct txq_info *txqi;
-- u8 tid;
--
-- spin_lock_bh(&air_sched->lock);
-- for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
-- if (air_info->weight == params->airtime_weight ||
-- !sta->sta.txq[tid] ||
-- ac != ieee80211_ac_from_tid(tid))
-- continue;
--
-- airtime_weight_set(air_info, params->airtime_weight);
--
-- txqi = to_txq_info(sta->sta.txq[tid]);
-- if (RB_EMPTY_NODE(&txqi->schedule_order))
-- continue;
--
-- ieee80211_update_airtime_weight(local, air_sched,
-- 0, true);
-- }
-- spin_unlock_bh(&air_sched->lock);
-- }
--}
--
- static int sta_apply_parameters(struct ieee80211_local *local,
- struct sta_info *sta,
- struct station_parameters *params)
-@@ -1773,8 +1741,7 @@ static int sta_apply_parameters(struct i
- sta_apply_mesh_params(local, sta, params);
-
- if (params->airtime_weight)
-- sta_apply_airtime_params(local, sta, params);
--
-+ sta->airtime_weight = params->airtime_weight;
-
- /* set the STA state after all sta info from usermode has been set */
- if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -216,14 +216,14 @@ static ssize_t aql_txq_limit_read(struct
- "VI %u %u\n"
- "BE %u %u\n"
- "BK %u %u\n",
-- local->airtime[IEEE80211_AC_VO].aql_txq_limit_low,
-- local->airtime[IEEE80211_AC_VO].aql_txq_limit_high,
-- local->airtime[IEEE80211_AC_VI].aql_txq_limit_low,
-- local->airtime[IEEE80211_AC_VI].aql_txq_limit_high,
-- local->airtime[IEEE80211_AC_BE].aql_txq_limit_low,
-- local->airtime[IEEE80211_AC_BE].aql_txq_limit_high,
-- local->airtime[IEEE80211_AC_BK].aql_txq_limit_low,
-- local->airtime[IEEE80211_AC_BK].aql_txq_limit_high);
-+ local->aql_txq_limit_low[IEEE80211_AC_VO],
-+ local->aql_txq_limit_high[IEEE80211_AC_VO],
-+ local->aql_txq_limit_low[IEEE80211_AC_VI],
-+ local->aql_txq_limit_high[IEEE80211_AC_VI],
-+ local->aql_txq_limit_low[IEEE80211_AC_BE],
-+ local->aql_txq_limit_high[IEEE80211_AC_BE],
-+ local->aql_txq_limit_low[IEEE80211_AC_BK],
-+ local->aql_txq_limit_high[IEEE80211_AC_BK]);
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
- }
-@@ -255,11 +255,11 @@ static ssize_t aql_txq_limit_write(struc
- if (ac >= IEEE80211_NUM_ACS)
- return -EINVAL;
-
-- q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
-- q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
-+ q_limit_low_old = local->aql_txq_limit_low[ac];
-+ q_limit_high_old = local->aql_txq_limit_high[ac];
-
-- local->airtime[ac].aql_txq_limit_low = q_limit_low;
-- local->airtime[ac].aql_txq_limit_high = q_limit_high;
-+ local->aql_txq_limit_low[ac] = q_limit_low;
-+ local->aql_txq_limit_high[ac] = q_limit_high;
-
- mutex_lock(&local->sta_mtx);
- list_for_each_entry(sta, &local->sta_list, list) {
-@@ -382,46 +382,6 @@ static const struct file_operations forc
- .llseek = default_llseek,
- };
-
--static ssize_t airtime_read(struct file *file,
-- char __user *user_buf,
-- size_t count,
-- loff_t *ppos)
--{
-- struct ieee80211_local *local = file->private_data;
-- char buf[200];
-- u64 v_t[IEEE80211_NUM_ACS];
-- u64 wt[IEEE80211_NUM_ACS];
-- int len = 0, ac;
--
-- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->airtime[ac].lock);
-- v_t[ac] = local->airtime[ac].v_t;
-- wt[ac] = local->airtime[ac].weight_sum;
-- spin_unlock_bh(&local->airtime[ac].lock);
-- }
-- len = scnprintf(buf, sizeof(buf),
-- "\tVO VI BE BK\n"
-- "Virt-t\t%-10llu %-10llu %-10llu %-10llu\n"
-- "Weight\t%-10llu %-10llu %-10llu %-10llu\n",
-- v_t[0],
-- v_t[1],
-- v_t[2],
-- v_t[3],
-- wt[0],
-- wt[1],
-- wt[2],
-- wt[3]);
--
-- return simple_read_from_buffer(user_buf, count, ppos,
-- buf, len);
--}
--
--static const struct file_operations airtime_ops = {
-- .read = airtime_read,
-- .open = simple_open,
-- .llseek = default_llseek,
--};
--
- #ifdef CONFIG_PM
- static ssize_t reset_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-@@ -672,11 +632,7 @@ void debugfs_hw_add(struct ieee80211_loc
- if (local->ops->wake_tx_queue)
- DEBUGFS_ADD_MODE(aqm, 0600);
-
-- if (wiphy_ext_feature_isset(local->hw.wiphy,
-- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
-- DEBUGFS_ADD_MODE(airtime, 0600);
-- DEBUGFS_ADD_MODE(airtime_flags, 0600);
-- }
-+ DEBUGFS_ADD_MODE(airtime_flags, 0600);
-
- DEBUGFS_ADD(aql_txq_limit);
- debugfs_create_u32("aql_threshold", 0600,
---- a/net/mac80211/debugfs_netdev.c
-+++ b/net/mac80211/debugfs_netdev.c
-@@ -512,34 +512,6 @@ static ssize_t ieee80211_if_fmt_aqm(
- }
- IEEE80211_IF_FILE_R(aqm);
-
--static ssize_t ieee80211_if_fmt_airtime(
-- const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
--{
-- struct ieee80211_local *local = sdata->local;
-- struct ieee80211_txq *txq = sdata->vif.txq;
-- struct airtime_info *air_info;
-- int len;
--
-- if (!txq)
-- return 0;
--
-- spin_lock_bh(&local->airtime[txq->ac].lock);
-- air_info = to_airtime_info(txq);
-- len = scnprintf(buf,
-- buflen,
-- "RX: %llu us\nTX: %llu us\nWeight: %u\n"
-- "Virt-T: %lld us\n",
-- air_info->rx_airtime,
-- air_info->tx_airtime,
-- air_info->weight,
-- air_info->v_t);
-- spin_unlock_bh(&local->airtime[txq->ac].lock);
--
-- return len;
--}
--
--IEEE80211_IF_FILE_R(airtime);
--
- IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
-
- /* IBSS attributes */
-@@ -685,10 +657,8 @@ static void add_common_files(struct ieee
-
- if (sdata->local->ops->wake_tx_queue &&
- sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-- sdata->vif.type != NL80211_IFTYPE_NAN) {
-+ sdata->vif.type != NL80211_IFTYPE_NAN)
- DEBUGFS_ADD(aqm);
-- DEBUGFS_ADD(airtime);
-- }
- }
-
- static void add_sta_files(struct ieee80211_sub_if_data *sdata)
---- a/net/mac80211/debugfs_sta.c
-+++ b/net/mac80211/debugfs_sta.c
-@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct f
- size_t bufsz = 400;
- char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
- u64 rx_airtime = 0, tx_airtime = 0;
-- u64 v_t[IEEE80211_NUM_ACS];
-+ s64 deficit[IEEE80211_NUM_ACS];
- ssize_t rv;
- int ac;
-
-@@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct f
- return -ENOMEM;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->airtime[ac].lock);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
- rx_airtime += sta->airtime[ac].rx_airtime;
- tx_airtime += sta->airtime[ac].tx_airtime;
-- v_t[ac] = sta->airtime[ac].v_t;
-- spin_unlock_bh(&local->airtime[ac].lock);
-+ deficit[ac] = sta->airtime[ac].deficit;
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
- }
-
- p += scnprintf(p, bufsz + buf - p,
- "RX: %llu us\nTX: %llu us\nWeight: %u\n"
-- "Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
-- rx_airtime, tx_airtime, sta->airtime[0].weight,
-- v_t[0], v_t[1], v_t[2], v_t[3]);
-+ "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
-+ rx_airtime, tx_airtime, sta->airtime_weight,
-+ deficit[0], deficit[1], deficit[2], deficit[3]);
-
- rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
- kfree(buf);
-@@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct
- int ac;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->airtime[ac].lock);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
- sta->airtime[ac].rx_airtime = 0;
- sta->airtime[ac].tx_airtime = 0;
-- sta->airtime[ac].v_t = 0;
-- spin_unlock_bh(&local->airtime[ac].lock);
-+ sta->airtime[ac].deficit = sta->airtime_weight;
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
- }
-
- return count;
-@@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file
- return -ENOMEM;
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-- spin_lock_bh(&local->airtime[ac].lock);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
- q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
- q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
-- spin_unlock_bh(&local->airtime[ac].lock);
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
- q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
- }
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -862,16 +862,20 @@ enum txq_info_flags {
- * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
- * a fq_flow which is already owned by a different tin
- * @def_cvars: codel vars for @def_flow
-- * @schedule_order: used with ieee80211_local->active_txqs
- * @frags: used to keep fragments created after dequeue
-+ * @schedule_order: used with ieee80211_local->active_txqs
-+ * @schedule_round: counter to prevent infinite loops on TXQ scheduling
- */
- struct txq_info {
- struct fq_tin tin;
- struct codel_vars def_cvars;
- struct codel_stats cstats;
-- struct rb_node schedule_order;
-+
-+ u16 schedule_round;
-+ struct list_head schedule_order;
-
- struct sk_buff_head frags;
-+
- unsigned long flags;
-
- /* keep last! */
-@@ -948,8 +952,6 @@ struct ieee80211_sub_if_data {
- struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
- struct mac80211_qos_map __rcu *qos_map;
-
-- struct airtime_info airtime[IEEE80211_NUM_ACS];
--
- struct work_struct csa_finalize_work;
- bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
- struct cfg80211_chan_def csa_chandef;
-@@ -1184,44 +1186,6 @@ enum mac80211_scan_state {
- SCAN_ABORT,
- };
-
--/**
-- * struct airtime_sched_info - state used for airtime scheduling and AQL
-- *
-- * @lock: spinlock that protects all the fields in this struct
-- * @active_txqs: rbtree of currently backlogged queues, sorted by virtual time
-- * @schedule_pos: the current position maintained while a driver walks the tree
-- * with ieee80211_next_txq()
-- * @active_list: list of struct airtime_info structs that were active within
-- * the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute
-- * weight_sum
-- * @last_weight_update: used for rate limiting walking active_list
-- * @last_schedule_time: tracks the last time a transmission was scheduled; used
-- * for catching up v_t if no stations are eligible for
-- * transmission.
-- * @v_t: global virtual time; queues with v_t < this are eligible for
-- * transmission
-- * @weight_sum: total sum of all active stations used for dividing airtime
-- * @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast
-- * path - see comment above
-- * IEEE80211_RECIPROCAL_DIVISOR_64)
-- * @aql_txq_limit_low: AQL limit when total outstanding airtime
-- * is < IEEE80211_AQL_THRESHOLD
-- * @aql_txq_limit_high: AQL limit when total outstanding airtime
-- * is > IEEE80211_AQL_THRESHOLD
-- */
--struct airtime_sched_info {
-- spinlock_t lock;
-- struct rb_root_cached active_txqs;
-- struct rb_node *schedule_pos;
-- struct list_head active_list;
-- u64 last_weight_update;
-- u64 last_schedule_activity;
-- u64 v_t;
-- u64 weight_sum;
-- u64 weight_sum_reciprocal;
-- u32 aql_txq_limit_low;
-- u32 aql_txq_limit_high;
--};
- DECLARE_STATIC_KEY_FALSE(aql_disable);
-
- struct ieee80211_local {
-@@ -1235,8 +1199,13 @@ struct ieee80211_local {
- struct codel_params cparams;
-
- /* protects active_txqs and txqi->schedule_order */
-- struct airtime_sched_info airtime[IEEE80211_NUM_ACS];
-+ spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
-+ struct list_head active_txqs[IEEE80211_NUM_ACS];
-+ u16 schedule_round[IEEE80211_NUM_ACS];
-+
- u16 airtime_flags;
-+ u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
-+ u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
- u32 aql_threshold;
- atomic_t aql_total_pending_airtime;
-
-@@ -1660,125 +1629,6 @@ static inline bool txq_has_queue(struct
- return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
- }
-
--static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq)
--{
-- struct ieee80211_sub_if_data *sdata;
-- struct sta_info *sta;
--
-- if (txq->sta) {
-- sta = container_of(txq->sta, struct sta_info, sta);
-- return &sta->airtime[txq->ac];
-- }
--
-- sdata = vif_to_sdata(txq->vif);
-- return &sdata->airtime[txq->ac];
--}
--
--/* To avoid divisions in the fast path, we keep pre-computed reciprocals for
-- * airtime weight calculations. There are two different weights to keep track
-- * of: The per-station weight and the sum of weights per phy.
-- *
-- * For the per-station weights (kept in airtime_info below), we use 32-bit
-- * reciprocals with a devisor of 2^19. This lets us keep the multiplications and
-- * divisions for the station weights as 32-bit operations at the cost of a bit
-- * of rounding error for high weights; but the choice of divisor keeps rounding
-- * errors <10% for weights <2^15, assuming no more than 8ms of airtime is
-- * reported at a time.
-- *
-- * For the per-phy sum of weights the values can get higher, so we use 64-bit
-- * operations for those with a 32-bit divisor, which should avoid any
-- * significant rounding errors.
-- */
--#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL
--#define IEEE80211_RECIPROCAL_SHIFT_64 32
--#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U
--#define IEEE80211_RECIPROCAL_SHIFT_32 19
--
--static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight)
--{
-- if (air_info->weight == weight)
-- return;
--
-- air_info->weight = weight;
-- if (weight) {
-- air_info->weight_reciprocal =
-- IEEE80211_RECIPROCAL_DIVISOR_32 / weight;
-- } else {
-- air_info->weight_reciprocal = 0;
-- }
--}
--
--static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched,
-- int weight_sum)
--{
-- if (air_sched->weight_sum == weight_sum)
-- return;
--
-- air_sched->weight_sum = weight_sum;
-- if (air_sched->weight_sum) {
-- air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64;
-- do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum);
-- } else {
-- air_sched->weight_sum_reciprocal = 0;
-- }
--}
--
--/* A problem when trying to enforce airtime fairness is that we want to divide
-- * the airtime between the currently *active* stations. However, basing this on
-- * the instantaneous queue state of stations doesn't work, as queues tend to
-- * oscillate very quickly between empty and occupied, leading to the scheduler
-- * thinking only a single station is active when deciding whether to allow
-- * transmission (and thus not throttling correctly).
-- *
-- * To fix this we use a timer-based notion of activity: a station is considered
-- * active if it has been scheduled within the last 100 ms; we keep a separate
-- * list of all the stations considered active in this manner, and lazily update
-- * the total weight of active stations from this list (filtering the stations in
-- * the list by their 'last active' time).
-- *
-- * We add one additional safeguard to guard against stations that manage to get
-- * scheduled every 100 ms but don't transmit a lot of data, and thus don't use
-- * up any airtime. Such stations would be able to get priority for an extended
-- * period of time if they do start transmitting at full capacity again, and so
-- * we add an explicit maximum for how far behind a station is allowed to fall in
-- * the virtual airtime domain. This limit is set to a relatively high value of
-- * 20 ms because the main mechanism for catching up idle stations is the active
-- * state as described above; i.e., the hard limit should only be hit in
-- * pathological cases.
-- */
--#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC)
--#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */
--
--static inline bool airtime_is_active(struct airtime_info *air_info, u64 now)
--{
-- return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION;
--}
--
--static inline void airtime_set_active(struct airtime_sched_info *air_sched,
-- struct airtime_info *air_info, u64 now)
--{
-- air_info->last_scheduled = now;
-- air_sched->last_schedule_activity = now;
-- list_move_tail(&air_info->list, &air_sched->active_list);
--}
--
--static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched,
-- u64 v_t, u64 now)
--{
-- air_sched->v_t = v_t;
-- return true;
--}
--
--static inline void init_airtime_info(struct airtime_info *air_info,
-- struct airtime_sched_info *air_sched)
--{
-- atomic_set(&air_info->aql_tx_pending, 0);
-- air_info->aql_limit_low = air_sched->aql_txq_limit_low;
-- air_info->aql_limit_high = air_sched->aql_txq_limit_high;
-- airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT);
-- INIT_LIST_HEAD(&air_info->list);
--}
--
- static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
- {
- return ether_addr_equal(raddr, addr) ||
-@@ -2024,14 +1874,6 @@ int ieee80211_tx_control_port(struct wip
- u64 *cookie);
- int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
- const u8 *buf, size_t len);
--void ieee80211_resort_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq);
--void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq,
-- bool purge);
--void ieee80211_update_airtime_weight(struct ieee80211_local *local,
-- struct airtime_sched_info *air_sched,
-- u64 now, bool force);
-
- /* HT */
- void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -2192,9 +2192,6 @@ int ieee80211_if_add(struct ieee80211_lo
- }
- }
-
-- for (i = 0; i < IEEE80211_NUM_ACS; i++)
-- init_airtime_info(&sdata->airtime[i], &local->airtime[i]);
--
- ieee80211_set_default_queues(sdata);
-
- sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -707,13 +707,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- spin_lock_init(&local->queue_stop_reason_lock);
-
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-- struct airtime_sched_info *air_sched = &local->airtime[i];
--
-- air_sched->active_txqs = RB_ROOT_CACHED;
-- INIT_LIST_HEAD(&air_sched->active_list);
-- spin_lock_init(&air_sched->lock);
-- air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
-- air_sched->aql_txq_limit_high =
-+ INIT_LIST_HEAD(&local->active_txqs[i]);
-+ spin_lock_init(&local->active_txq_lock[i]);
-+ local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
-+ local->aql_txq_limit_high[i] =
- IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
- }
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -1583,8 +1583,12 @@ static void sta_ps_start(struct sta_info
-
- for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
- struct ieee80211_txq *txq = sta->sta.txq[tid];
-+ struct txq_info *txqi = to_txq_info(txq);
-
-- ieee80211_unschedule_txq(&local->hw, txq, false);
-+ spin_lock(&local->active_txq_lock[txq->ac]);
-+ if (!list_empty(&txqi->schedule_order))
-+ list_del_init(&txqi->schedule_order);
-+ spin_unlock(&local->active_txq_lock[txq->ac]);
-
- if (txq_has_queue(txq))
- set_bit(tid, &sta->txq_buffered_tids);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -426,11 +426,15 @@ struct sta_info *sta_info_alloc(struct i
- if (sta_prepare_rate_control(local, sta, gfp))
- goto free_txq;
-
-+ sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
-
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- skb_queue_head_init(&sta->ps_tx_buf[i]);
- skb_queue_head_init(&sta->tx_filtered[i]);
-- init_airtime_info(&sta->airtime[i], &local->airtime[i]);
-+ sta->airtime[i].deficit = sta->airtime_weight;
-+ atomic_set(&sta->airtime[i].aql_tx_pending, 0);
-+ sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
-+ sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
- }
-
- for (i = 0; i < IEEE80211_NUM_TIDS; i++)
-@@ -1889,59 +1893,24 @@ void ieee80211_sta_set_buffered(struct i
- }
- EXPORT_SYMBOL(ieee80211_sta_set_buffered);
-
--void ieee80211_register_airtime(struct ieee80211_txq *txq,
-- u32 tx_airtime, u32 rx_airtime)
-+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
-+ u32 tx_airtime, u32 rx_airtime)
- {
-- struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
-- struct ieee80211_local *local = sdata->local;
-- u64 weight_sum, weight_sum_reciprocal;
-- struct airtime_sched_info *air_sched;
-- struct airtime_info *air_info;
-+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
-+ struct ieee80211_local *local = sta->sdata->local;
-+ u8 ac = ieee80211_ac_from_tid(tid);
- u32 airtime = 0;
-
-- air_sched = &local->airtime[txq->ac];
-- air_info = to_airtime_info(txq);
--
-- if (local->airtime_flags & AIRTIME_USE_TX)
-+ if (sta->local->airtime_flags & AIRTIME_USE_TX)
- airtime += tx_airtime;
-- if (local->airtime_flags & AIRTIME_USE_RX)
-+ if (sta->local->airtime_flags & AIRTIME_USE_RX)
- airtime += rx_airtime;
-
-- /* Weights scale so the unit weight is 256 */
-- airtime <<= 8;
--
-- spin_lock_bh(&air_sched->lock);
--
-- air_info->tx_airtime += tx_airtime;
-- air_info->rx_airtime += rx_airtime;
--
-- if (air_sched->weight_sum) {
-- weight_sum = air_sched->weight_sum;
-- weight_sum_reciprocal = air_sched->weight_sum_reciprocal;
-- } else {
-- weight_sum = air_info->weight;
-- weight_sum_reciprocal = air_info->weight_reciprocal;
-- }
--
-- /* Round the calculation of global vt */
-- air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
-- weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
-- air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
-- air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
-- ieee80211_resort_txq(&local->hw, txq);
--
-- spin_unlock_bh(&air_sched->lock);
--}
--
--void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
-- u32 tx_airtime, u32 rx_airtime)
--{
-- struct ieee80211_txq *txq = pubsta->txq[tid];
--
-- if (!txq)
-- return;
--
-- ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
-+ sta->airtime[ac].tx_airtime += tx_airtime;
-+ sta->airtime[ac].rx_airtime += rx_airtime;
-+ sta->airtime[ac].deficit -= airtime;
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
- }
- EXPORT_SYMBOL(ieee80211_sta_register_airtime);
-
-@@ -2385,7 +2354,7 @@ void sta_set_sinfo(struct sta_info *sta,
- }
-
- if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
-- sinfo->airtime_weight = sta->airtime[0].weight;
-+ sinfo->airtime_weight = sta->airtime_weight;
- sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
- }
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -135,25 +135,18 @@ enum ieee80211_agg_stop_reason {
- #define AIRTIME_USE_TX BIT(0)
- #define AIRTIME_USE_RX BIT(1)
-
--
- struct airtime_info {
- u64 rx_airtime;
- u64 tx_airtime;
-- u64 v_t;
-- u64 last_scheduled;
-- struct list_head list;
-+ s64 deficit;
- atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
- u32 aql_limit_low;
- u32 aql_limit_high;
-- u32 weight_reciprocal;
-- u16 weight;
- };
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
- u16 tx_airtime, bool tx_completed);
--void ieee80211_register_airtime(struct ieee80211_txq *txq,
-- u32 tx_airtime, u32 rx_airtime);
-
- struct sta_info;
-
-@@ -523,6 +516,7 @@ struct ieee80211_fragment_cache {
- * @tid_seq: per-TID sequence numbers for sending to this STA
- * @airtime: per-AC struct airtime_info describing airtime statistics for this
- * station
-+ * @airtime_weight: station weight for airtime fairness calculation purposes
- * @ampdu_mlme: A-MPDU state machine state
- * @mesh: mesh STA information
- * @debugfs_dir: debug filesystem directory dentry
-@@ -653,6 +647,7 @@ struct sta_info {
- u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
-
- struct airtime_info airtime[IEEE80211_NUM_ACS];
-+ u16 airtime_weight;
-
- /*
- * Aggregation information, locked with lock.
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -983,25 +983,6 @@ static void __ieee80211_tx_status(struct
- if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
- ieee80211_frame_acked(sta, skb);
-
-- } else if (wiphy_ext_feature_isset(local->hw.wiphy,
-- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
-- struct ieee80211_sub_if_data *sdata;
-- struct ieee80211_txq *txq;
-- u32 airtime;
--
-- /* Account airtime to multicast queue */
-- sdata = ieee80211_sdata_from_skb(local, skb);
--
-- if (sdata && (txq = sdata->vif.txq)) {
-- airtime = info->status.tx_time ?:
-- ieee80211_calc_expected_tx_airtime(hw,
-- &sdata->vif,
-- NULL,
-- skb->len,
-- false);
--
-- ieee80211_register_airtime(txq, airtime, 0);
-- }
- }
-
- /* SNMP counters
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -18,7 +18,6 @@
- #include <linux/bitmap.h>
- #include <linux/rcupdate.h>
- #include <linux/export.h>
--#include <linux/timekeeping.h>
- #include <net/net_namespace.h>
- #include <net/ieee80211_radiotap.h>
- #include <net/cfg80211.h>
-@@ -1480,7 +1479,7 @@ void ieee80211_txq_init(struct ieee80211
- codel_vars_init(&txqi->def_cvars);
- codel_stats_init(&txqi->cstats);
- __skb_queue_head_init(&txqi->frags);
-- RB_CLEAR_NODE(&txqi->schedule_order);
-+ INIT_LIST_HEAD(&txqi->schedule_order);
-
- txqi->txq.vif = &sdata->vif;
-
-@@ -1524,7 +1523,9 @@ void ieee80211_txq_purge(struct ieee8021
- ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
- spin_unlock_bh(&fq->lock);
-
-- ieee80211_unschedule_txq(&local->hw, &txqi->txq, true);
-+ spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
-+ list_del_init(&txqi->schedule_order);
-+ spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
- }
-
- void ieee80211_txq_set_params(struct ieee80211_local *local)
-@@ -3819,259 +3820,102 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
- struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-- struct airtime_sched_info *air_sched;
-- u64 now = ktime_get_coarse_boottime_ns();
- struct ieee80211_txq *ret = NULL;
-- struct airtime_info *air_info;
-- struct txq_info *txqi = NULL;
-- struct rb_node *node;
-- bool first = false;
-+ struct txq_info *txqi = NULL, *head = NULL;
-+ bool found_eligible_txq = false;
-
-- air_sched = &local->airtime[ac];
-- spin_lock_bh(&air_sched->lock);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
-
-- node = air_sched->schedule_pos;
--
--begin:
-- if (!node) {
-- node = rb_first_cached(&air_sched->active_txqs);
-- first = true;
-- } else {
-- node = rb_next(node);
-- }
--
-- if (!node)
-- goto out;
--
-- txqi = container_of(node, struct txq_info, schedule_order);
-- air_info = to_airtime_info(&txqi->txq);
--
-- if (air_info->v_t > air_sched->v_t &&
-- (!first || !airtime_catchup_v_t(air_sched, air_info->v_t, now)))
-+ begin:
-+ txqi = list_first_entry_or_null(&local->active_txqs[ac],
-+ struct txq_info,
-+ schedule_order);
-+ if (!txqi)
- goto out;
-
-- if (!ieee80211_txq_airtime_check(hw, &txqi->txq)) {
-- first = false;
-- goto begin;
-- }
--
-- air_sched->schedule_pos = node;
-- air_sched->last_schedule_activity = now;
-- ret = &txqi->txq;
--out:
-- spin_unlock_bh(&air_sched->lock);
-- return ret;
--}
--EXPORT_SYMBOL(ieee80211_next_txq);
--
--static void __ieee80211_insert_txq(struct rb_root_cached *root,
-- struct txq_info *txqi)
--{
-- struct rb_node **new = &root->rb_root.rb_node;
-- struct airtime_info *old_air, *new_air;
-- struct rb_node *parent = NULL;
-- struct txq_info *__txqi;
-- bool leftmost = true;
--
-- while (*new) {
-- parent = *new;
-- __txqi = rb_entry(parent, struct txq_info, schedule_order);
-- old_air = to_airtime_info(&__txqi->txq);
-- new_air = to_airtime_info(&txqi->txq);
--
-- if (new_air->v_t <= old_air->v_t) {
-- new = &parent->rb_left;
-- } else {
-- new = &parent->rb_right;
-- leftmost = false;
-- }
-+ if (txqi == head) {
-+ if (!found_eligible_txq)
-+ goto out;
-+ else
-+ found_eligible_txq = false;
- }
-
-- rb_link_node(&txqi->schedule_order, parent, new);
-- rb_insert_color_cached(&txqi->schedule_order, root, leftmost);
--}
--
--void ieee80211_resort_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq)
--{
-- struct airtime_info *air_info = to_airtime_info(txq);
-- struct ieee80211_local *local = hw_to_local(hw);
-- struct txq_info *txqi = to_txq_info(txq);
-- struct airtime_sched_info *air_sched;
--
-- air_sched = &local->airtime[txq->ac];
-+ if (!head)
-+ head = txqi;
-
-- lockdep_assert_held(&air_sched->lock);
--
-- if (!RB_EMPTY_NODE(&txqi->schedule_order)) {
-- struct airtime_info *a_prev = NULL, *a_next = NULL;
-- struct txq_info *t_prev, *t_next;
-- struct rb_node *n_prev, *n_next;
-+ if (txqi->txq.sta) {
-+ struct sta_info *sta = container_of(txqi->txq.sta,
-+ struct sta_info, sta);
-+ bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-+ s64 deficit = sta->airtime[txqi->txq.ac].deficit;
-
-- /* Erasing a node can cause an expensive rebalancing operation,
-- * so we check the previous and next nodes first and only remove
-- * and re-insert if the current node is not already in the
-- * correct position.
-- */
-- if ((n_prev = rb_prev(&txqi->schedule_order)) != NULL) {
-- t_prev = container_of(n_prev, struct txq_info,
-- schedule_order);
-- a_prev = to_airtime_info(&t_prev->txq);
-- }
-+ if (aql_check)
-+ found_eligible_txq = true;
-
-- if ((n_next = rb_next(&txqi->schedule_order)) != NULL) {
-- t_next = container_of(n_next, struct txq_info,
-- schedule_order);
-- a_next = to_airtime_info(&t_next->txq);
-+ if (deficit < 0)
-+ sta->airtime[txqi->txq.ac].deficit +=
-+ sta->airtime_weight;
-+
-+ if (deficit < 0 || !aql_check) {
-+ list_move_tail(&txqi->schedule_order,
-+ &local->active_txqs[txqi->txq.ac]);
-+ goto begin;
- }
--
-- if ((!a_prev || a_prev->v_t <= air_info->v_t) &&
-- (!a_next || a_next->v_t > air_info->v_t))
-- return;
--
-- if (air_sched->schedule_pos == &txqi->schedule_order)
-- air_sched->schedule_pos = n_prev;
--
-- rb_erase_cached(&txqi->schedule_order,
-- &air_sched->active_txqs);
-- RB_CLEAR_NODE(&txqi->schedule_order);
-- __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
- }
--}
--
--void ieee80211_update_airtime_weight(struct ieee80211_local *local,
-- struct airtime_sched_info *air_sched,
-- u64 now, bool force)
--{
-- struct airtime_info *air_info, *tmp;
-- u64 weight_sum = 0;
--
-- if (unlikely(!now))
-- now = ktime_get_coarse_boottime_ns();
--
-- lockdep_assert_held(&air_sched->lock);
--
-- if (!force && (air_sched->last_weight_update <
-- now - AIRTIME_ACTIVE_DURATION))
-- return;
--
-- list_for_each_entry_safe(air_info, tmp,
-- &air_sched->active_list, list) {
-- if (airtime_is_active(air_info, now))
-- weight_sum += air_info->weight;
-- else
-- list_del_init(&air_info->list);
-- }
-- airtime_weight_sum_set(air_sched, weight_sum);
-- air_sched->last_weight_update = now;
--}
-
--void ieee80211_schedule_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq)
-- __acquires(txq_lock) __releases(txq_lock)
--{
-- struct ieee80211_local *local = hw_to_local(hw);
-- struct txq_info *txqi = to_txq_info(txq);
-- struct airtime_sched_info *air_sched;
-- u64 now = ktime_get_coarse_boottime_ns();
-- struct airtime_info *air_info;
-- u8 ac = txq->ac;
-- bool was_active;
-
-- air_sched = &local->airtime[ac];
-- air_info = to_airtime_info(txq);
--
-- spin_lock_bh(&air_sched->lock);
-- was_active = airtime_is_active(air_info, now);
-- airtime_set_active(air_sched, air_info, now);
--
-- if (!RB_EMPTY_NODE(&txqi->schedule_order))
-+ if (txqi->schedule_round == local->schedule_round[ac])
- goto out;
-
-- /* If the station has been inactive for a while, catch up its v_t so it
-- * doesn't get indefinite priority; see comment above the definition of
-- * AIRTIME_MAX_BEHIND.
-- */
-- if ((!was_active && air_info->v_t < air_sched->v_t) ||
-- air_info->v_t < air_sched->v_t - AIRTIME_MAX_BEHIND)
-- air_info->v_t = air_sched->v_t;
--
-- ieee80211_update_airtime_weight(local, air_sched, now, !was_active);
-- __ieee80211_insert_txq(&air_sched->active_txqs, txqi);
-+ list_del_init(&txqi->schedule_order);
-+ txqi->schedule_round = local->schedule_round[ac];
-+ ret = &txqi->txq;
-
- out:
-- spin_unlock_bh(&air_sched->lock);
--}
--EXPORT_SYMBOL(ieee80211_schedule_txq);
--
--static void __ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq,
-- bool purge)
--{
-- struct ieee80211_local *local = hw_to_local(hw);
-- struct txq_info *txqi = to_txq_info(txq);
-- struct airtime_sched_info *air_sched;
-- struct airtime_info *air_info;
--
-- air_sched = &local->airtime[txq->ac];
-- air_info = to_airtime_info(&txqi->txq);
--
-- lockdep_assert_held(&air_sched->lock);
--
-- if (purge) {
-- list_del_init(&air_info->list);
-- ieee80211_update_airtime_weight(local, air_sched, 0, true);
-- }
--
-- if (RB_EMPTY_NODE(&txqi->schedule_order))
-- return;
--
-- if (air_sched->schedule_pos == &txqi->schedule_order)
-- air_sched->schedule_pos = rb_prev(&txqi->schedule_order);
--
-- if (!purge)
-- airtime_set_active(air_sched, air_info,
-- ktime_get_coarse_boottime_ns());
--
-- rb_erase_cached(&txqi->schedule_order,
-- &air_sched->active_txqs);
-- RB_CLEAR_NODE(&txqi->schedule_order);
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
-+ return ret;
- }
-+EXPORT_SYMBOL(ieee80211_next_txq);
-
--void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
-+void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq,
-- bool purge)
-- __acquires(txq_lock) __releases(txq_lock)
--{
-- struct ieee80211_local *local = hw_to_local(hw);
--
-- spin_lock_bh(&local->airtime[txq->ac].lock);
-- __ieee80211_unschedule_txq(hw, txq, purge);
-- spin_unlock_bh(&local->airtime[txq->ac].lock);
--}
--
--void ieee80211_return_txq(struct ieee80211_hw *hw,
-- struct ieee80211_txq *txq, bool force)
-+ bool force)
- {
- struct ieee80211_local *local = hw_to_local(hw);
- struct txq_info *txqi = to_txq_info(txq);
-
-- spin_lock_bh(&local->airtime[txq->ac].lock);
-+ spin_lock_bh(&local->active_txq_lock[txq->ac]);
-
-- if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force &&
-- !txq_has_queue(txq))
-- __ieee80211_unschedule_txq(hw, txq, false);
-+ if (list_empty(&txqi->schedule_order) &&
-+ (force || !skb_queue_empty(&txqi->frags) ||
-+ txqi->tin.backlog_packets)) {
-+ /* If airtime accounting is active, always enqueue STAs at the
-+ * head of the list to ensure that they only get moved to the
-+ * back by the airtime DRR scheduler once they have a negative
-+ * deficit. A station that already has a negative deficit will
-+ * get immediately moved to the back of the list on the next
-+ * call to ieee80211_next_txq().
-+ */
-+ if (txqi->txq.sta && local->airtime_flags &&
-+ wiphy_ext_feature_isset(local->hw.wiphy,
-+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
-+ list_add(&txqi->schedule_order,
-+ &local->active_txqs[txq->ac]);
-+ else
-+ list_add_tail(&txqi->schedule_order,
-+ &local->active_txqs[txq->ac]);
-+ }
-
-- spin_unlock_bh(&local->airtime[txq->ac].lock);
-+ spin_unlock_bh(&local->active_txq_lock[txq->ac]);
- }
--EXPORT_SYMBOL(ieee80211_return_txq);
-+EXPORT_SYMBOL(__ieee80211_schedule_txq);
-
- DEFINE_STATIC_KEY_FALSE(aql_disable);
-
- bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
- {
-- struct airtime_info *air_info = to_airtime_info(txq);
-+ struct sta_info *sta;
- struct ieee80211_local *local = hw_to_local(hw);
-
- if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
-@@ -4086,12 +3930,15 @@ bool ieee80211_txq_airtime_check(struct
- if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
- return true;
-
-- if (atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_low)
-+ sta = container_of(txq->sta, struct sta_info, sta);
-+ if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
-+ sta->airtime[txq->ac].aql_limit_low)
- return true;
-
- if (atomic_read(&local->aql_total_pending_airtime) <
- local->aql_threshold &&
-- atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high)
-+ atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
-+ sta->airtime[txq->ac].aql_limit_high)
- return true;
-
- return false;
-@@ -4101,59 +3948,60 @@ EXPORT_SYMBOL(ieee80211_txq_airtime_chec
- bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
- {
-- struct txq_info *first_txqi = NULL, *txqi = to_txq_info(txq);
- struct ieee80211_local *local = hw_to_local(hw);
-- struct airtime_sched_info *air_sched;
-- struct airtime_info *air_info;
-- struct rb_node *node = NULL;
-- bool ret = false;
-- u64 now;
--
-+ struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
-+ struct sta_info *sta;
-+ u8 ac = txq->ac;
-
-- if (!ieee80211_txq_airtime_check(hw, txq))
-- return false;
-+ spin_lock_bh(&local->active_txq_lock[ac]);
-
-- air_sched = &local->airtime[txq->ac];
-- spin_lock_bh(&air_sched->lock);
-+ if (!txqi->txq.sta)
-+ goto out;
-
-- if (RB_EMPTY_NODE(&txqi->schedule_order))
-+ if (list_empty(&txqi->schedule_order))
- goto out;
-
-- now = ktime_get_coarse_boottime_ns();
-+ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
-+ schedule_order) {
-+ if (iter == txqi)
-+ break;
-
-- /* Like in ieee80211_next_txq(), make sure the first station in the
-- * scheduling order is eligible for transmission to avoid starvation.
-- */
-- node = rb_first_cached(&air_sched->active_txqs);
-- if (node) {
-- first_txqi = container_of(node, struct txq_info,
-- schedule_order);
-- air_info = to_airtime_info(&first_txqi->txq);
--
-- if (air_sched->v_t < air_info->v_t)
-- airtime_catchup_v_t(air_sched, air_info->v_t, now);
-+ if (!iter->txq.sta) {
-+ list_move_tail(&iter->schedule_order,
-+ &local->active_txqs[ac]);
-+ continue;
-+ }
-+ sta = container_of(iter->txq.sta, struct sta_info, sta);
-+ if (sta->airtime[ac].deficit < 0)
-+ sta->airtime[ac].deficit += sta->airtime_weight;
-+ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
- }
-
-- air_info = to_airtime_info(&txqi->txq);
-- if (air_info->v_t <= air_sched->v_t) {
-- air_sched->last_schedule_activity = now;
-- ret = true;
-- }
-+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
-+ if (sta->airtime[ac].deficit >= 0)
-+ goto out;
-+
-+ sta->airtime[ac].deficit += sta->airtime_weight;
-+ list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
-
-+ return false;
- out:
-- spin_unlock_bh(&air_sched->lock);
-- return ret;
-+ if (!list_empty(&txqi->schedule_order))
-+ list_del_init(&txqi->schedule_order);
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
-+
-+ return true;
- }
- EXPORT_SYMBOL(ieee80211_txq_may_transmit);
-
- void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-- struct airtime_sched_info *air_sched = &local->airtime[ac];
-
-- spin_lock_bh(&air_sched->lock);
-- air_sched->schedule_pos = NULL;
-- spin_unlock_bh(&air_sched->lock);
-+ spin_lock_bh(&local->active_txq_lock[ac]);
-+ local->schedule_round[ac]++;
-+ spin_unlock_bh(&local->active_txq_lock[ac]);
- }
- EXPORT_SYMBOL(ieee80211_txq_schedule_start);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Jun 2022 14:53:04 +0200
-Subject: [PATCH] mac80211: make sta airtime deficit field s32 instead of
- s64
-
-32 bit is more than enough range for the airtime deficit
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/debugfs_sta.c
-+++ b/net/mac80211/debugfs_sta.c
-@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct f
- size_t bufsz = 400;
- char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
- u64 rx_airtime = 0, tx_airtime = 0;
-- s64 deficit[IEEE80211_NUM_ACS];
-+ s32 deficit[IEEE80211_NUM_ACS];
- ssize_t rv;
- int ac;
-
-@@ -219,7 +219,7 @@ static ssize_t sta_airtime_read(struct f
-
- p += scnprintf(p, bufsz + buf - p,
- "RX: %llu us\nTX: %llu us\nWeight: %u\n"
-- "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
-+ "Deficit: VO: %d us VI: %d us BE: %d us BK: %d us\n",
- rx_airtime, tx_airtime, sta->airtime_weight,
- deficit[0], deficit[1], deficit[2], deficit[3]);
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -138,7 +138,7 @@ enum ieee80211_agg_stop_reason {
- struct airtime_info {
- u64 rx_airtime;
- u64 tx_airtime;
-- s64 deficit;
-+ s32 deficit;
- atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
- u32 aql_limit_low;
- u32 aql_limit_high;
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3847,7 +3847,7 @@ struct ieee80211_txq *ieee80211_next_txq
- struct sta_info *sta = container_of(txqi->txq.sta,
- struct sta_info, sta);
- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-- s64 deficit = sta->airtime[txqi->txq.ac].deficit;
-+ s32 deficit = sta->airtime[txqi->txq.ac].deficit;
-
- if (aql_check)
- found_eligible_txq = true;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Jun 2022 14:59:09 +0200
-Subject: [PATCH] mac80211: consider aql_tx_pending when checking airtime
- deficit
-
-When queueing packets for a station, deficit only gets added once the packets
-have been transmitted, which could be much later. During that time, a lot of
-temporary unfairness could happen, which could lead to bursty behavior.
-Fix this by subtracting the aql_tx_pending when checking the deficit in tx
-scheduling.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3817,6 +3817,13 @@ out:
- }
- EXPORT_SYMBOL(ieee80211_tx_dequeue);
-
-+static inline s32 ieee80211_sta_deficit(struct sta_info *sta, u8 ac)
-+{
-+ struct airtime_info *air_info = &sta->airtime[ac];
-+
-+ return air_info->deficit - atomic_read(&air_info->aql_tx_pending);
-+}
-+
- struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-@@ -3847,7 +3854,7 @@ struct ieee80211_txq *ieee80211_next_txq
- struct sta_info *sta = container_of(txqi->txq.sta,
- struct sta_info, sta);
- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-- s32 deficit = sta->airtime[txqi->txq.ac].deficit;
-+ s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
-
- if (aql_check)
- found_eligible_txq = true;
-@@ -3972,7 +3979,7 @@ bool ieee80211_txq_may_transmit(struct i
- continue;
- }
- sta = container_of(iter->txq.sta, struct sta_info, sta);
-- if (sta->airtime[ac].deficit < 0)
-+ if (ieee80211_sta_deficit(sta, ac) < 0)
- sta->airtime[ac].deficit += sta->airtime_weight;
- list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
- }
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Jun 2022 20:52:50 +0200
-Subject: [PATCH] mac80211: keep recently active tx queues in scheduling
- list
-
-This allows proper deficit accounting to ensure that they don't carry their
-deficit until the next time they become active
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -83,6 +83,13 @@ extern const u8 ieee80211_ac_to_qos_mask
-
- #define IEEE80211_MAX_NAN_INSTANCE_ID 255
-
-+
-+/*
-+ * Keep a station's queues on the active list for deficit accounting purposes
-+ * if it was active or queued during the last 100ms
-+ */
-+#define AIRTIME_ACTIVE_DURATION (HZ / 10)
-+
- struct ieee80211_bss {
- u32 device_ts_beacon, device_ts_presp;
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -138,6 +138,7 @@ enum ieee80211_agg_stop_reason {
- struct airtime_info {
- u64 rx_airtime;
- u64 tx_airtime;
-+ u32 last_active;
- s32 deficit;
- atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
- u32 aql_limit_low;
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3824,6 +3824,36 @@ static inline s32 ieee80211_sta_deficit(
- return air_info->deficit - atomic_read(&air_info->aql_tx_pending);
- }
-
-+static void
-+ieee80211_txq_set_active(struct txq_info *txqi)
-+{
-+ struct sta_info *sta;
-+
-+ if (!txqi->txq.sta)
-+ return;
-+
-+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
-+ sta->airtime[txqi->txq.ac].last_active = (u32)jiffies;
-+}
-+
-+static bool
-+ieee80211_txq_keep_active(struct txq_info *txqi)
-+{
-+ struct sta_info *sta;
-+ u32 diff;
-+
-+ if (!txqi->txq.sta)
-+ return false;
-+
-+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
-+ if (ieee80211_sta_deficit(sta, txqi->txq.ac) >= 0)
-+ return false;
-+
-+ diff = (u32)jiffies - sta->airtime[txqi->txq.ac].last_active;
-+
-+ return diff <= AIRTIME_ACTIVE_DURATION;
-+}
-+
- struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-@@ -3870,7 +3900,6 @@ struct ieee80211_txq *ieee80211_next_txq
- }
- }
-
--
- if (txqi->schedule_round == local->schedule_round[ac])
- goto out;
-
-@@ -3890,12 +3919,13 @@ void __ieee80211_schedule_txq(struct iee
- {
- struct ieee80211_local *local = hw_to_local(hw);
- struct txq_info *txqi = to_txq_info(txq);
-+ bool has_queue;
-
- spin_lock_bh(&local->active_txq_lock[txq->ac]);
-
-+ has_queue = force || txq_has_queue(txq);
- if (list_empty(&txqi->schedule_order) &&
-- (force || !skb_queue_empty(&txqi->frags) ||
-- txqi->tin.backlog_packets)) {
-+ (has_queue || ieee80211_txq_keep_active(txqi))) {
- /* If airtime accounting is active, always enqueue STAs at the
- * head of the list to ensure that they only get moved to the
- * back by the airtime DRR scheduler once they have a negative
-@@ -3903,7 +3933,7 @@ void __ieee80211_schedule_txq(struct iee
- * get immediately moved to the back of the list on the next
- * call to ieee80211_next_txq().
- */
-- if (txqi->txq.sta && local->airtime_flags &&
-+ if (txqi->txq.sta && local->airtime_flags && has_queue &&
- wiphy_ext_feature_isset(local->hw.wiphy,
- NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
- list_add(&txqi->schedule_order,
-@@ -3911,6 +3941,8 @@ void __ieee80211_schedule_txq(struct iee
- else
- list_add_tail(&txqi->schedule_order,
- &local->active_txqs[txq->ac]);
-+ if (has_queue)
-+ ieee80211_txq_set_active(txqi);
- }
-
- spin_unlock_bh(&local->active_txq_lock[txq->ac]);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 20 Jun 2022 21:26:34 +0200
-Subject: [PATCH] mac80211: add a per-PHY AQL limit to improve fairness
-
-In order to maintain fairness, the amount of queueing needs to be limited
-beyond the simple per-station AQL budget, otherwise the driver can simply
-repeatedly do scheduling rounds until all queues that have not used their
-AQL budget become eligble.
-
-To be conservative, use the high AQL limit for the first txq and add half
-of the low AQL for each subsequent queue.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1215,6 +1215,7 @@ struct ieee80211_local {
- u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
- u32 aql_threshold;
- atomic_t aql_total_pending_airtime;
-+ atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
-
- const struct ieee80211_ops *ops;
-
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -712,6 +712,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
- local->aql_txq_limit_high[i] =
- IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
-+ atomic_set(&local->aql_ac_pending_airtime[i], 0);
- }
-
- local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -1929,6 +1929,7 @@ void ieee80211_sta_update_pending_airtim
- &sta->airtime[ac].aql_tx_pending);
-
- atomic_add(tx_airtime, &local->aql_total_pending_airtime);
-+ atomic_add(tx_airtime, &local->aql_ac_pending_airtime[ac]);
- return;
- }
-
-@@ -1940,14 +1941,17 @@ void ieee80211_sta_update_pending_airtim
- tx_pending, 0);
- }
-
-+ atomic_sub(tx_airtime, &local->aql_total_pending_airtime);
- tx_pending = atomic_sub_return(tx_airtime,
-- &local->aql_total_pending_airtime);
-+ &local->aql_ac_pending_airtime[ac]);
- if (WARN_ONCE(tx_pending < 0,
- "Device %s AC %d pending airtime underflow: %u, %u",
- wiphy_name(local->hw.wiphy), ac, tx_pending,
-- tx_airtime))
-- atomic_cmpxchg(&local->aql_total_pending_airtime,
-+ tx_airtime)) {
-+ atomic_cmpxchg(&local->aql_ac_pending_airtime[ac],
- tx_pending, 0);
-+ atomic_sub(tx_pending, &local->aql_total_pending_airtime);
-+ }
- }
-
- int sta_info_move_state(struct sta_info *sta,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3863,6 +3863,9 @@ struct ieee80211_txq *ieee80211_next_txq
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-
-+ if (!local->schedule_round[ac])
-+ goto out;
-+
- begin:
- txqi = list_first_entry_or_null(&local->active_txqs[ac],
- struct txq_info,
-@@ -3984,6 +3987,25 @@ bool ieee80211_txq_airtime_check(struct
- }
- EXPORT_SYMBOL(ieee80211_txq_airtime_check);
-
-+static bool
-+ieee80211_txq_schedule_airtime_check(struct ieee80211_local *local, u8 ac)
-+{
-+ unsigned int num_txq = 0;
-+ struct txq_info *txq;
-+ u32 aql_limit;
-+
-+ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
-+ return true;
-+
-+ list_for_each_entry(txq, &local->active_txqs[ac], schedule_order)
-+ num_txq++;
-+
-+ aql_limit = (num_txq - 1) * local->aql_txq_limit_low[ac] / 2 +
-+ local->aql_txq_limit_high[ac];
-+
-+ return atomic_read(&local->aql_ac_pending_airtime[ac]) < aql_limit;
-+}
-+
- bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
- {
-@@ -4000,6 +4022,9 @@ bool ieee80211_txq_may_transmit(struct i
- if (list_empty(&txqi->schedule_order))
- goto out;
-
-+ if (!ieee80211_txq_schedule_airtime_check(local, ac))
-+ goto out;
-+
- list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
- schedule_order) {
- if (iter == txqi)
-@@ -4039,7 +4064,15 @@ void ieee80211_txq_schedule_start(struct
- struct ieee80211_local *local = hw_to_local(hw);
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-- local->schedule_round[ac]++;
-+
-+ if (ieee80211_txq_schedule_airtime_check(local, ac)) {
-+ local->schedule_round[ac]++;
-+ if (!local->schedule_round[ac])
-+ local->schedule_round[ac]++;
-+ } else {
-+ local->schedule_round[ac] = 0;
-+ }
-+
- spin_unlock_bh(&local->active_txq_lock[ac]);
- }
- EXPORT_SYMBOL(ieee80211_txq_schedule_start);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 25 Jun 2022 21:25:40 +0200
-Subject: [PATCH] mac80211: add debugfs file to display per-phy AQL pending
- airtime
-
-Now that the global pending airtime is more relevant for airtime fairness,
-it makes sense to make it accessible via debugfs for debugging
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -201,6 +201,36 @@ static const struct file_operations airt
- .llseek = default_llseek,
- };
-
-+static ssize_t aql_pending_read(struct file *file,
-+ char __user *user_buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct ieee80211_local *local = file->private_data;
-+ char buf[400];
-+ int len = 0;
-+
-+ len = scnprintf(buf, sizeof(buf),
-+ "AC AQL pending\n"
-+ "VO %u us\n"
-+ "VI %u us\n"
-+ "BE %u us\n"
-+ "BK %u us\n"
-+ "total %u us\n",
-+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
-+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
-+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
-+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
-+ atomic_read(&local->aql_total_pending_airtime));
-+ return simple_read_from_buffer(user_buf, count, ppos,
-+ buf, len);
-+}
-+
-+static const struct file_operations aql_pending_ops = {
-+ .read = aql_pending_read,
-+ .open = simple_open,
-+ .llseek = default_llseek,
-+};
-+
- static ssize_t aql_txq_limit_read(struct file *file,
- char __user *user_buf,
- size_t count,
-@@ -628,6 +658,7 @@ void debugfs_hw_add(struct ieee80211_loc
- DEBUGFS_ADD(hw_conf);
- DEBUGFS_ADD_MODE(force_tx_status, 0600);
- DEBUGFS_ADD_MODE(aql_enable, 0600);
-+ DEBUGFS_ADD(aql_pending);
-
- if (local->ops->wake_tx_queue)
- DEBUGFS_ADD_MODE(aqm, 0600);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 25 Jun 2022 23:10:19 +0200
-Subject: [PATCH] mac80211: only accumulate airtime deficit for active
- clients
-
-When a client does not generate any local tx activity, accumulating airtime
-deficit for the round-robin scheduler can be harmful. If this goes on for too
-long, the deficit could grow quite large, which might cause unreasonable
-initial latency once the client becomes active
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -1900,6 +1900,7 @@ void ieee80211_sta_register_airtime(stru
- struct ieee80211_local *local = sta->sdata->local;
- u8 ac = ieee80211_ac_from_tid(tid);
- u32 airtime = 0;
-+ u32 diff;
-
- if (sta->local->airtime_flags & AIRTIME_USE_TX)
- airtime += tx_airtime;
-@@ -1909,7 +1910,11 @@ void ieee80211_sta_register_airtime(stru
- spin_lock_bh(&local->active_txq_lock[ac]);
- sta->airtime[ac].tx_airtime += tx_airtime;
- sta->airtime[ac].rx_airtime += rx_airtime;
-- sta->airtime[ac].deficit -= airtime;
-+
-+ diff = (u32)jiffies - sta->airtime[ac].last_active;
-+ if (diff <= AIRTIME_ACTIVE_DURATION)
-+ sta->airtime[ac].deficit -= airtime;
-+
- spin_unlock_bh(&local->active_txq_lock[ac]);
- }
- EXPORT_SYMBOL(ieee80211_sta_register_airtime);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 26 Jun 2022 11:43:25 +0200
-Subject: [PATCH] mac80211: increase quantum for airtime scheduler
-
-Given the typical AQL budget and queue length, a quantum of 256 with the
-default station weight often requires iterating over all queues frequently,
-until one of them becomes eligible.
-Improve performance by using 8 times station weight as scheduler quantum
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -90,6 +90,8 @@ extern const u8 ieee80211_ac_to_qos_mask
- */
- #define AIRTIME_ACTIVE_DURATION (HZ / 10)
-
-+#define AIRTIME_QUANTUM_SHIFT 3
-+
- struct ieee80211_bss {
- u32 device_ts_beacon, device_ts_presp;
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3894,7 +3894,7 @@ struct ieee80211_txq *ieee80211_next_txq
-
- if (deficit < 0)
- sta->airtime[txqi->txq.ac].deficit +=
-- sta->airtime_weight;
-+ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
-
- if (deficit < 0 || !aql_check) {
- list_move_tail(&txqi->schedule_order,
-@@ -4037,7 +4037,8 @@ bool ieee80211_txq_may_transmit(struct i
- }
- sta = container_of(iter->txq.sta, struct sta_info, sta);
- if (ieee80211_sta_deficit(sta, ac) < 0)
-- sta->airtime[ac].deficit += sta->airtime_weight;
-+ sta->airtime[ac].deficit += sta->airtime_weight <<
-+ AIRTIME_QUANTUM_SHIFT;
- list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
- }
-
-@@ -4045,7 +4046,7 @@ bool ieee80211_txq_may_transmit(struct i
- if (sta->airtime[ac].deficit >= 0)
- goto out;
-
-- sta->airtime[ac].deficit += sta->airtime_weight;
-+ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
- list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
- spin_unlock_bh(&local->active_txq_lock[ac]);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 13 Jul 2022 07:32:26 +0200
-Subject: [PATCH] mac80211: exclude multicast packets from AQL pending airtime
-
-In AP mode, multicast traffic is handled very differently from normal traffic,
-especially if at least one client is in powersave mode.
-This means that multicast packets can be buffered a lot longer than normal
-unicast packets, and can eat up the AQL budget very quickly because of the low
-data rate.
-Along with the recent change to maintain a global PHY AQL limit, this can lead
-to significant latency spikes for unicast traffic.
-
-Since queueing multicast to hardware is currently not constrained by AQL limits
-anyway, let's just exclude it from the AQL pending airtime calculation entirely.
-
-Fixes: 8e4bac067105 ("wifi: mac80211: add a per-PHY AQL limit to improve fairness")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -3792,7 +3792,7 @@ begin:
- encap_out:
- IEEE80211_SKB_CB(skb)->control.vif = vif;
-
-- if (vif &&
-+ if (tx.sta &&
- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
- bool ampdu = txq->ac != IEEE80211_AC_VO;
- u32 airtime;
+++ /dev/null
-From aa40d5a43526cca9439a2b45fcfdcd016594dece Mon Sep 17 00:00:00 2001
-From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
-Date: Sun, 17 Jul 2022 21:21:52 +0900
-Subject: [PATCH] wifi: mac80211: do not abuse fq.lock in ieee80211_do_stop()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-lockdep complains use of uninitialized spinlock at ieee80211_do_stop() [1],
-for commit f856373e2f31ffd3 ("wifi: mac80211: do not wake queues on a vif
-that is being stopped") guards clear_bit() using fq.lock even before
-fq_init() from ieee80211_txq_setup_flows() initializes this spinlock.
-
-According to discussion [2], Toke was not happy with expanding usage of
-fq.lock. Since __ieee80211_wake_txqs() is called under RCU read lock, we
-can instead use synchronize_rcu() for flushing ieee80211_wake_txqs().
-
-Link: https://syzkaller.appspot.com/bug?extid=eceab52db7c4b961e9d6 [1]
-Link: https://lkml.kernel.org/r/874k0zowh2.fsf@toke.dk [2]
-Reported-by: syzbot <syzbot+eceab52db7c4b961e9d6@syzkaller.appspotmail.com>
-Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
-Fixes: f856373e2f31ffd3 ("wifi: mac80211: do not wake queues on a vif that is being stopped")
-Tested-by: syzbot <syzbot+eceab52db7c4b961e9d6@syzkaller.appspotmail.com>
-Acked-by: Toke Høiland-Jørgensen <toke@kernel.org>
-Signed-off-by: Kalle Valo <kvalo@kernel.org>
-Link: https://lore.kernel.org/r/9cc9b81d-75a3-3925-b612-9d0ad3cab82b@I-love.SAKURA.ne.jp
-[ pick up commit 3598cb6e1862 ("wifi: mac80211: do not abuse fq.lock in ieee80211_do_stop()") from -next]
-Link: https://lore.kernel.org/all/87o7xcq6qt.fsf@kernel.org/
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
----
- net/mac80211/iface.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -377,9 +377,8 @@ static void ieee80211_do_stop(struct iee
- bool cancel_scan;
- struct cfg80211_nan_func *func;
-
-- spin_lock_bh(&local->fq.lock);
- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
-- spin_unlock_bh(&local->fq.lock);
-+ synchronize_rcu(); /* flush _ieee80211_wake_txqs() */
-
- cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
- if (cancel_scan)
+++ /dev/null
-From: Alexander Wetzel <alexander@wetzel-home.de>
-Date: Thu, 15 Sep 2022 14:41:20 +0200
-Subject: [PATCH] mac80211: Fix deadlock: Don't start TX while holding
- fq->lock
-
-ieee80211_txq_purge() calls fq_tin_reset() and
-ieee80211_purge_tx_queue(); Both are then calling
-ieee80211_free_txskb(). Which can decide to TX the skb again.
-
-There are at least two ways to get a deadlock:
-
-1) When we have a TDLS teardown packet queued in either tin or frags
- ieee80211_tdls_td_tx_handle() will call ieee80211_subif_start_xmit()
- while we still hold fq->lock. ieee80211_txq_enqueue() will thus
- deadlock.
-
-2) A variant of the above happens if aggregation is up and running:
- In that case ieee80211_iface_work() will deadlock with the original
- task: The original tasks already holds fq->lock and tries to get
- sta->lock after kicking off ieee80211_iface_work(). But the worker
- can get sta->lock prior to the original task and will then spin for
- fq->lock.
-
-Avoid these deadlocks by not sending out any skbs when called via
-ieee80211_free_txskb().
-
-Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
----
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -698,7 +698,7 @@ static void ieee80211_report_used_skb(st
-
- if (!sdata) {
- skb->dev = NULL;
-- } else {
-+ } else if (!dropped) {
- unsigned int hdr_size =
- ieee80211_hdrlen(hdr->frame_control);
-
+++ /dev/null
-From: Alexander Wetzel <alexander@wetzel-home.de>
-Date: Thu, 15 Sep 2022 15:09:46 +0200
-Subject: [PATCH] mac80211: Ensure vif queues are operational after start
-
-Make sure local->queue_stop_reasons and vif.txqs_stopped stay in sync.
-
-When a new vif is created the queues may end up in an inconsistent state
-and be inoperable:
-Communication not using iTXQ will work, allowing to e.g. complete the
-association. But the 4-way handshake will time out. The sta will not
-send out any skbs queued in iTXQs.
-
-All normal attempts to start the queues will fail when reaching this
-state.
-local->queue_stop_reasons will have marked all queues as operational but
-vif.txqs_stopped will still be set, creating an inconsistent internal
-state.
-
-In reality this seems to be race between the mac80211 function
-ieee80211_do_open() setting SDATA_STATE_RUNNING and the wake_txqs_tasklet:
-Depending on the driver and the timing the queues may end up to be
-operational or not.
-
-Cc: stable@vger.kernel.org
-Fixes: f856373e2f31 ("wifi: mac80211: do not wake queues on a vif that is being stopped")
-Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
----
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -301,14 +301,14 @@ static void __ieee80211_wake_txqs(struct
- local_bh_disable();
- spin_lock(&fq->lock);
-
-+ sdata->vif.txqs_stopped[ac] = false;
-+
- if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
- goto out;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP)
- ps = &sdata->bss->ps;
-
-- sdata->vif.txqs_stopped[ac] = false;
--
- list_for_each_entry_rcu(sta, &local->sta_list, list) {
- if (sdata != sta->sdata)
- continue;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 28 Sep 2022 13:50:34 +0200
-Subject: [PATCH] wifi: mac80211: fix decap offload for stations on AP_VLAN
- interfaces
-
-Since AP_VLAN interfaces are not passed to the driver, check offload_flags
-on the bss vif instead.
-
-Reported-by: Howard Hsu <howard-yh.hsu@mediatek.com>
-Fixes: 80a915ec4427 ("mac80211: add rx decapsulation offload support")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -4267,6 +4267,7 @@ void ieee80211_check_fast_rx(struct sta_
- .vif_type = sdata->vif.type,
- .control_port_protocol = sdata->control_port_protocol,
- }, *old, *new = NULL;
-+ u32 offload_flags;
- bool set_offload = false;
- bool assign = false;
- bool offload;
-@@ -4382,10 +4383,10 @@ void ieee80211_check_fast_rx(struct sta_
- if (assign)
- new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL);
-
-- offload = assign &&
-- (sdata->vif.offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED);
-+ offload_flags = get_bss_sdata(sdata)->vif.offload_flags;
-+ offload = offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED;
-
-- if (offload)
-+ if (assign && offload)
- set_offload = !test_and_set_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD);
- else
- set_offload = test_and_clear_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 7 Oct 2022 10:54:47 +0200
-Subject: [PATCH] wifi: cfg80211: fix ieee80211_data_to_8023_exthdr
- handling of small packets
-
-STP topology change notification packets only have a payload of 7 bytes,
-so they get dropped due to the skb->len < hdrlen + 8 check.
-Fix this by removing skb->len based checks and instead check the return code
-on the skb_copy_bits calls.
-
-Fixes: 2d1c304cb2d5 ("cfg80211: add function for 802.3 conversion with separate output buffer")
-Reported-by: Chad Monroe <chad.monroe@smartrg.com>
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -557,7 +557,7 @@ int ieee80211_data_to_8023_exthdr(struct
- return -1;
-
- hdrlen = ieee80211_hdrlen(hdr->frame_control) + data_offset;
-- if (skb->len < hdrlen + 8)
-+ if (skb->len < hdrlen)
- return -1;
-
- /* convert IEEE 802.11 header + possible LLC headers into Ethernet
-@@ -572,8 +572,9 @@ int ieee80211_data_to_8023_exthdr(struct
- memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
- memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
-
-- if (iftype == NL80211_IFTYPE_MESH_POINT)
-- skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
-+ if (iftype == NL80211_IFTYPE_MESH_POINT &&
-+ skb_copy_bits(skb, hdrlen, &mesh_flags, 1) < 0)
-+ return -1;
-
- mesh_flags &= MESH_FLAGS_AE;
-
-@@ -593,11 +594,12 @@ int ieee80211_data_to_8023_exthdr(struct
- if (iftype == NL80211_IFTYPE_MESH_POINT) {
- if (mesh_flags == MESH_FLAGS_AE_A4)
- return -1;
-- if (mesh_flags == MESH_FLAGS_AE_A5_A6) {
-- skb_copy_bits(skb, hdrlen +
-- offsetof(struct ieee80211s_hdr, eaddr1),
-- tmp.h_dest, 2 * ETH_ALEN);
-- }
-+ if (mesh_flags == MESH_FLAGS_AE_A5_A6 &&
-+ skb_copy_bits(skb, hdrlen +
-+ offsetof(struct ieee80211s_hdr, eaddr1),
-+ tmp.h_dest, 2 * ETH_ALEN) < 0)
-+ return -1;
-+
- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
- }
- break;
-@@ -611,10 +613,11 @@ int ieee80211_data_to_8023_exthdr(struct
- if (iftype == NL80211_IFTYPE_MESH_POINT) {
- if (mesh_flags == MESH_FLAGS_AE_A5_A6)
- return -1;
-- if (mesh_flags == MESH_FLAGS_AE_A4)
-- skb_copy_bits(skb, hdrlen +
-- offsetof(struct ieee80211s_hdr, eaddr1),
-- tmp.h_source, ETH_ALEN);
-+ if (mesh_flags == MESH_FLAGS_AE_A4 &&
-+ skb_copy_bits(skb, hdrlen +
-+ offsetof(struct ieee80211s_hdr, eaddr1),
-+ tmp.h_source, ETH_ALEN) < 0)
-+ return -1;
- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
- }
- break;
-@@ -626,18 +629,18 @@ int ieee80211_data_to_8023_exthdr(struct
- break;
- }
-
-- skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
-- tmp.h_proto = payload.proto;
--
-- if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
-- tmp.h_proto != htons(ETH_P_AARP) &&
-- tmp.h_proto != htons(ETH_P_IPX)) ||
-- ether_addr_equal(payload.hdr, bridge_tunnel_header)))
-+ if (likely(skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
-+ ((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
-+ payload.proto != htons(ETH_P_AARP) &&
-+ payload.proto != htons(ETH_P_IPX)) ||
-+ ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation and
- * replace EtherType */
- hdrlen += ETH_ALEN + 2;
-- else
-+ tmp.h_proto = payload.proto;
-+ } else {
- tmp.h_proto = htons(skb->len - hdrlen);
-+ }
-
- pskb_pull(skb, hdrlen);
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 7 Oct 2022 10:58:26 +0200
-Subject: [PATCH] wifi: mac80211: do not drop packets smaller than the
- LLC-SNAP header on fast-rx
-
-Since STP TCN frames are only 7 bytes, the pskb_may_pull call returns an error.
-Instead of dropping those packets, bump them back to the slow path for proper
-processing.
-
-Fixes: 49ddf8e6e234 ("mac80211: add fast-rx path")
-Reported-by: Chad Monroe <chad.monroe@smartrg.com>
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -4603,7 +4603,7 @@ static bool ieee80211_invoke_fast_rx(str
-
- if (!(status->rx_flags & IEEE80211_RX_AMSDU)) {
- if (!pskb_may_pull(skb, snap_offs + sizeof(*payload)))
-- goto drop;
-+ return false;
-
- payload = (void *)(skb->data + snap_offs);
-
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 1 Jul 2022 19:06:11 +0530
-Subject: [PATCH] wifi: mac80211: fix mesh airtime link metric estimating
-
-ieee80211s_update_metric function uses sta_set_rate_info_tx
-function to get struct rate_info data from ieee80211_tx_rate
-struct, present in ieee80211_sta->deflink.tx_stats. However,
-drivers can skip tx rate calculation by setting rate idx as
--1. Such drivers provides rate_info directly and hence
-ieee80211s metric is updated incorrectly since ieee80211_tx_rate
-has inconsistent data.
-
-Add fix to use rate_info directly if present instead of
-sta_set_rate_info_tx for updating ieee80211s metric.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://lore.kernel.org/r/20220701133611.544-1-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/mesh_hwmp.c
-+++ b/net/mac80211/mesh_hwmp.c
-@@ -310,7 +310,12 @@ void ieee80211s_update_metric(struct iee
- LINK_FAIL_THRESH)
- mesh_plink_broken(sta);
-
-- sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
-+ /* use rate info set by the driver directly if present */
-+ if (st->rate)
-+ rinfo = sta->tx_stats.last_rate_info;
-+ else
-+ sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
-+
- ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg,
- cfg80211_calculate_bitrate(&rinfo));
- }
+++ /dev/null
-From 6d945a33f2b0aa24fc210dadaa0af3e8218e7002 Mon Sep 17 00:00:00 2001
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Fri, 25 Mar 2022 11:42:41 +0100
-Subject: [PATCH] mac80211: introduce BSS color collision detection
-
-Add ieee80211_rx_check_bss_color_collision routine in order to introduce
-BSS color collision detection in mac80211 if it is not supported in HW/FW
-(e.g. for mt7915 chipset).
-Add IEEE80211_HW_DETECTS_COLOR_COLLISION flag to let the driver notify
-BSS color collision detection is supported in HW/FW. Set this for ath11k
-which apparently didn't need this code.
-
-Tested-by: Peter Chiu <Chui-Hao.Chiu@mediatek.com>
-Co-developed-by: Ryder Lee <ryder.lee@mediatek.com>
-Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Link: https://lore.kernel.org/r/a05eeeb1841a84560dc5aaec77894fcb69a54f27.1648204871.git.lorenzo@kernel.org
-[clarify commit message a bit, move flag to mac80211]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
- drivers/net/wireless/ath/ath11k/mac.c | 5 ++-
- include/net/mac80211.h | 4 +++
- net/mac80211/debugfs.c | 1 +
- net/mac80211/rx.c | 46 +++++++++++++++++++++++++++
- 4 files changed, 55 insertions(+), 1 deletion(-)
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -2418,6 +2418,9 @@ struct ieee80211_txq {
- * usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to
- * the stack.
- *
-+ * @IEEE80211_HW_DETECTS_COLOR_COLLISION: HW/driver has support for BSS color
-+ * collision detection and doesn't need it in software.
-+ *
- * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
- */
- enum ieee80211_hw_flags {
-@@ -2473,6 +2476,7 @@ enum ieee80211_hw_flags {
- IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
- IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
- IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
-+ IEEE80211_HW_DETECTS_COLOR_COLLISION,
-
- /* keep last, obviously */
- NUM_IEEE80211_HW_FLAGS
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -494,6 +494,7 @@ static const char *hw_flag_names[] = {
- FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
- FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
- FLAG(SUPPORTS_CONC_MON_RX_DECAP),
-+ FLAG(DETECTS_COLOR_COLLISION),
- #undef FLAG
- };
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -3182,6 +3182,49 @@ static void ieee80211_process_sa_query_r
- ieee80211_tx_skb(sdata, skb);
- }
-
-+static void
-+ieee80211_rx_check_bss_color_collision(struct ieee80211_rx_data *rx)
-+{
-+ struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;
-+ const struct element *ie;
-+ size_t baselen;
-+
-+ if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
-+ NL80211_EXT_FEATURE_BSS_COLOR))
-+ return;
-+
-+ if (ieee80211_hw_check(&rx->local->hw, DETECTS_COLOR_COLLISION))
-+ return;
-+
-+ if (rx->sdata->vif.csa_active)
-+ return;
-+
-+ baselen = mgmt->u.beacon.variable - rx->skb->data;
-+ if (baselen > rx->skb->len)
-+ return;
-+
-+ ie = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION,
-+ mgmt->u.beacon.variable,
-+ rx->skb->len - baselen);
-+ if (ie && ie->datalen >= sizeof(struct ieee80211_he_operation) &&
-+ ie->datalen >= ieee80211_he_oper_size(ie->data + 1)) {
-+ struct ieee80211_bss_conf *bss_conf = &rx->sdata->vif.bss_conf;
-+ const struct ieee80211_he_operation *he_oper;
-+ u8 color;
-+
-+ he_oper = (void *)(ie->data + 1);
-+ if (le32_get_bits(he_oper->he_oper_params,
-+ IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED))
-+ return;
-+
-+ color = le32_get_bits(he_oper->he_oper_params,
-+ IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
-+ if (color == bss_conf->he_bss_color.color)
-+ ieeee80211_obss_color_collision_notify(&rx->sdata->vif,
-+ BIT_ULL(color));
-+ }
-+}
-+
- static ieee80211_rx_result debug_noinline
- ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
- {
-@@ -3207,6 +3250,9 @@ ieee80211_rx_h_mgmt_check(struct ieee802
- !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) {
- int sig = 0;
-
-+ /* sw bss color collision detection */
-+ ieee80211_rx_check_bss_color_collision(rx);
-+
- if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) &&
- !(status->flag & RX_FLAG_NO_SIGNAL_VAL))
- sig = status->signal;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 9 Oct 2022 20:15:46 +0200
-Subject: [PATCH] mac80211: add support for restricting netdev features per vif
-
-This can be used to selectively disable feature flags for checksum offload,
-scatter/gather or GSO by changing vif->netdev_features.
-Removing features from vif->netdev_features does not affect the netdev
-features themselves, but instead fixes up skbs in the tx path so that the
-offloads are not needed in the driver.
-
-Aside from making it easier to deal with vif type based hardware limitations,
-this also makes it possible to optimize performance on hardware without native
-GSO support by declaring GSO support in hw->netdev_features and removing it
-from vif->netdev_features. This allows mac80211 to handle GSO segmentation
-after the sta lookup, but before itxq enqueue, thus reducing the number of
-unnecessary sta lookups, as well as some other per-packet processing.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/fq_impl.h
-+++ b/include/net/fq_impl.h
-@@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq
- fq_skb_free_t free_func)
- {
- struct fq_flow *flow;
-+ struct sk_buff *next;
- bool oom;
-
- lockdep_assert_held(&fq->lock);
-@@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq
- }
-
- flow->tin = tin;
-- flow->backlog += skb->len;
-- tin->backlog_bytes += skb->len;
-- tin->backlog_packets++;
-- fq->memory_usage += skb->truesize;
-- fq->backlog++;
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ flow->backlog += skb->len;
-+ tin->backlog_bytes += skb->len;
-+ tin->backlog_packets++;
-+ fq->memory_usage += skb->truesize;
-+ fq->backlog++;
-+ __skb_queue_tail(&flow->queue, skb);
-+ }
-
- if (list_empty(&flow->flowchain)) {
- flow->deficit = fq->quantum;
-@@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq
- &tin->new_flows);
- }
-
-- __skb_queue_tail(&flow->queue, skb);
- oom = (fq->memory_usage > fq->memory_limit);
- while (fq->backlog > fq->limit || oom) {
- flow = fq_find_fattest_flow(fq);
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1685,6 +1685,10 @@ enum ieee80211_offload_flags {
- * write-protected by sdata_lock and local->mtx so holding either is fine
- * for read access.
- * @mu_mimo_owner: indicates interface owns MU-MIMO capability
-+ * @netdev_features: tx netdev features supported by the hardware for this
-+ * vif. mac80211 initializes this to hw->netdev_features, and the driver
-+ * can mask out specific tx features. mac80211 will handle software fixup
-+ * for masked offloads (GSO, CSUM)
- * @driver_flags: flags/capabilities the driver has for this interface,
- * these need to be set (or cleared) when the interface is added
- * or, if supported by the driver, the interface type is changed
-@@ -1736,6 +1740,7 @@ struct ieee80211_vif {
-
- struct ieee80211_chanctx_conf __rcu *chanctx_conf;
-
-+ netdev_features_t netdev_features;
- u32 driver_flags;
- u32 offload_flags;
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -2209,6 +2209,7 @@ int ieee80211_if_add(struct ieee80211_lo
- ndev->features |= local->hw.netdev_features;
- ndev->hw_features |= ndev->features &
- MAC80211_SUPPORTED_FEATURES_TX;
-+ sdata->vif.netdev_features = local->hw.netdev_features;
-
- netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1310,7 +1310,11 @@ static struct txq_info *ieee80211_get_tx
-
- static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
- {
-- IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
-+ struct sk_buff *next;
-+ codel_time_t now = codel_get_time();
-+
-+ skb_list_walk_safe(skb, skb, next)
-+ IEEE80211_SKB_CB(skb)->control.enqueue_time = now;
- }
-
- static u32 codel_skb_len_func(const struct sk_buff *skb)
-@@ -3499,47 +3503,71 @@ ieee80211_xmit_fast_finish(struct ieee80
- return TX_CONTINUE;
- }
-
--static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta,
-- struct ieee80211_fast_tx *fast_tx,
-- struct sk_buff *skb)
-+static netdev_features_t
-+ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata)
- {
-- struct ieee80211_local *local = sdata->local;
-- u16 ethertype = (skb->data[12] << 8) | skb->data[13];
-- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
-- int hw_headroom = sdata->local->hw.extra_tx_headroom;
-- struct ethhdr eth;
-- struct ieee80211_tx_info *info;
-- struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-- struct ieee80211_tx_data tx;
-- ieee80211_tx_result r;
-- struct tid_ampdu_tx *tid_tx = NULL;
-- u8 tid = IEEE80211_NUM_TIDS;
-+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
-+ return sdata->vif.netdev_features;
-
-- /* control port protocol needs a lot of special handling */
-- if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
-- return false;
-+ if (!sdata->bss)
-+ return 0;
-
-- /* only RFC 1042 SNAP */
-- if (ethertype < ETH_P_802_3_MIN)
-- return false;
-+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
-+ return sdata->vif.netdev_features;
-+}
-
-- /* don't handle TX status request here either */
-- if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
-- return false;
-+static struct sk_buff *
-+ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features)
-+{
-+ if (skb_is_gso(skb)) {
-+ struct sk_buff *segs;
-
-- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-- if (tid_tx) {
-- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
-- return false;
-- if (tid_tx->timeout)
-- tid_tx->last_tx = jiffies;
-- }
-+ segs = skb_gso_segment(skb, features);
-+ if (!segs)
-+ return skb;
-+ if (IS_ERR(segs))
-+ goto free;
-+
-+ consume_skb(skb);
-+ return segs;
-+ }
-+
-+ if (skb_needs_linearize(skb, features) && __skb_linearize(skb))
-+ goto free;
-+
-+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
-+ int ofs = skb_checksum_start_offset(skb);
-+
-+ if (skb->encapsulation)
-+ skb_set_inner_transport_header(skb, ofs);
-+ else
-+ skb_set_transport_header(skb, ofs);
-+
-+ if (skb_csum_hwoffload_help(skb, features))
-+ goto free;
- }
-
-- /* after this point (skb is modified) we cannot return false */
-+ skb_mark_not_on_list(skb);
-+ return skb;
-+
-+free:
-+ kfree_skb(skb);
-+ return NULL;
-+}
-+
-+static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct ieee80211_fast_tx *fast_tx,
-+ struct sk_buff *skb, u8 tid, bool ampdu)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+ struct ieee80211_tx_info *info;
-+ struct ieee80211_tx_data tx;
-+ ieee80211_tx_result r;
-+ int hw_headroom = sdata->local->hw.extra_tx_headroom;
-+ int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
-+ struct ethhdr eth;
-
- if (skb_shared(skb)) {
- struct sk_buff *tmp_skb = skb;
-@@ -3548,12 +3576,12 @@ static bool ieee80211_xmit_fast(struct i
- kfree_skb(tmp_skb);
-
- if (!skb)
-- return true;
-+ return;
- }
-
- if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
- ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
-- return true;
-+ return;
-
- /* will not be crypto-handled beyond what we do here, so use false
- * as the may-encrypt argument for the resize to not account for
-@@ -3562,10 +3590,8 @@ static bool ieee80211_xmit_fast(struct i
- if (unlikely(ieee80211_skb_resize(sdata, skb,
- max_t(int, extra_head + hw_headroom -
- skb_headroom(skb), 0),
-- ENCRYPT_NO))) {
-- kfree_skb(skb);
-- return true;
-- }
-+ ENCRYPT_NO)))
-+ goto free;
-
- memcpy(ð, skb->data, ETH_HLEN - 2);
- hdr = skb_push(skb, extra_head);
-@@ -3579,7 +3605,7 @@ static bool ieee80211_xmit_fast(struct i
- info->control.vif = &sdata->vif;
- info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
- IEEE80211_TX_CTL_DONTFRAG |
-- (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
-+ (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
- info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
-
- #ifdef CPTCFG_MAC80211_DEBUGFS
-@@ -3601,16 +3627,14 @@ static bool ieee80211_xmit_fast(struct i
- tx.key = fast_tx->key;
-
- if (ieee80211_queue_skb(local, sdata, sta, skb))
-- return true;
-+ return;
-
- tx.skb = skb;
- r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
- fast_tx->key, &tx);
- tx.skb = NULL;
-- if (r == TX_DROP) {
-- kfree_skb(skb);
-- return true;
-- }
-+ if (r == TX_DROP)
-+ goto free;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -3618,6 +3642,55 @@ static bool ieee80211_xmit_fast(struct i
-
- __skb_queue_tail(&tx.skbs, skb);
- ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
-+ return;
-+
-+free:
-+ kfree_skb(skb);
-+}
-+
-+static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct ieee80211_fast_tx *fast_tx,
-+ struct sk_buff *skb)
-+{
-+ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
-+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+ struct tid_ampdu_tx *tid_tx = NULL;
-+ struct sk_buff *next;
-+ u8 tid = IEEE80211_NUM_TIDS;
-+
-+ /* control port protocol needs a lot of special handling */
-+ if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
-+ return false;
-+
-+ /* only RFC 1042 SNAP */
-+ if (ethertype < ETH_P_802_3_MIN)
-+ return false;
-+
-+ /* don't handle TX status request here either */
-+ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
-+ return false;
-+
-+ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-+ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-+ if (tid_tx) {
-+ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
-+ return false;
-+ if (tid_tx->timeout)
-+ tid_tx->last_tx = jiffies;
-+ }
-+ }
-+
-+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
-+ if (!skb)
-+ return true;
-+
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
-+ }
-+
- return true;
- }
-
-@@ -4123,31 +4196,14 @@ void __ieee80211_subif_start_xmit(struct
- goto out;
- }
-
-- if (skb_is_gso(skb)) {
-- struct sk_buff *segs;
--
-- segs = skb_gso_segment(skb, 0);
-- if (IS_ERR(segs)) {
-- goto out_free;
-- } else if (segs) {
-- consume_skb(skb);
-- skb = segs;
-- }
-- } else {
-- /* we cannot process non-linear frames on this path */
-- if (skb_linearize(skb))
-- goto out_free;
--
-- /* the frame could be fragmented, software-encrypted, and other
-- * things so we cannot really handle checksum offload with it -
-- * fix it up in software before we handle anything else.
-- */
-- if (skb->ip_summed == CHECKSUM_PARTIAL) {
-- skb_set_transport_header(skb,
-- skb_checksum_start_offset(skb));
-- if (skb_checksum_help(skb))
-- goto out_free;
-- }
-+ /* the frame could be fragmented, software-encrypted, and other
-+ * things so we cannot really handle checksum or GSO offload.
-+ * fix it up in software before we handle anything else.
-+ */
-+ skb = ieee80211_tx_skb_fixup(skb, 0);
-+ if (!skb) {
-+ len = 0;
-+ goto out;
- }
-
- skb_list_walk_safe(skb, skb, next) {
-@@ -4310,9 +4366,11 @@ netdev_tx_t ieee80211_subif_start_xmit(s
- return NETDEV_TX_OK;
- }
-
--static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-- struct sk_buff *skb, struct sta_info *sta,
-- bool txpending)
-+
-+
-+static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct sta_info *sta,
-+ bool txpending)
- {
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_tx_control control = {};
-@@ -4321,14 +4379,6 @@ static bool ieee80211_tx_8023(struct iee
- unsigned long flags;
- int q = info->hw_queue;
-
-- if (sta)
-- sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift);
--
-- ieee80211_tpt_led_trig_tx(local, skb->len);
--
-- if (ieee80211_queue_skb(local, sdata, sta, skb))
-- return true;
--
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-
- if (local->queue_stop_reasons[q] ||
-@@ -4355,27 +4405,50 @@ static bool ieee80211_tx_8023(struct iee
- return true;
- }
-
-+static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct sta_info *sta,
-+ bool txpending)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct sk_buff *next;
-+ bool ret = true;
-+
-+ if (ieee80211_queue_skb(local, sdata, sta, skb))
-+ return true;
-+
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ if (!__ieee80211_tx_8023(sdata, skb, sta, txpending))
-+ ret = false;
-+ }
-+
-+ return ret;
-+}
-+
- static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- struct net_device *dev, struct sta_info *sta,
- struct ieee80211_key *key, struct sk_buff *skb)
- {
-- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ struct ieee80211_tx_info *info;
- struct ieee80211_local *local = sdata->local;
- struct tid_ampdu_tx *tid_tx;
-+ struct sk_buff *seg, *next;
-+ unsigned int skbs = 0, len = 0;
-+ u16 queue;
- u8 tid;
-
- if (local->ops->wake_tx_queue) {
-- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
-+ queue = __ieee80211_select_queue(sdata, sta, skb);
- skb_set_queue_mapping(skb, queue);
- skb_get_hash(skb);
-+ } else {
-+ queue = skb_get_queue_mapping(skb);
- }
-
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
- goto out_free;
-
-- memset(info, 0, sizeof(*info));
--
- ieee80211_aggr_check(sdata, sta, skb);
-
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-@@ -4387,22 +4460,20 @@ static void ieee80211_8023_xmit(struct i
- return;
- }
-
-- info->flags |= IEEE80211_TX_CTL_AMPDU;
- if (tid_tx->timeout)
- tid_tx->last_tx = jiffies;
- }
-
-- if (unlikely(skb->sk &&
-- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
-- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
-- &info->flags, NULL);
-+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
-+ if (!skb)
-+ return;
-
-- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-+ info = IEEE80211_SKB_CB(skb);
-+ memset(info, 0, sizeof(*info));
-+ if (tid_tx)
-+ info->flags |= IEEE80211_TX_CTL_AMPDU;
-
-- dev_sw_netstats_tx_add(dev, 1, skb->len);
--
-- sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-- sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-+ info->hw_queue = sdata->vif.hw_queue[queue];
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -4414,6 +4485,24 @@ static void ieee80211_8023_xmit(struct i
- if (key)
- info->control.hw_key = &key->conf;
-
-+ skb_list_walk_safe(skb, seg, next) {
-+ skbs++;
-+ len += seg->len;
-+ if (seg != skb)
-+ memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info));
-+ }
-+
-+ if (unlikely(skb->sk &&
-+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
-+ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
-+ &info->flags, NULL);
-+
-+ dev_sw_netstats_tx_add(dev, skbs, len);
-+ sta->tx_stats.packets[queue] += skbs;
-+ sta->tx_stats.bytes[queue] += len;
-+
-+ ieee80211_tpt_led_trig_tx(local, len);
-+
- ieee80211_tx_8023(sdata, skb, sta, false);
-
- return;
-@@ -4455,6 +4544,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
- key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
- goto skip_offload;
-
-+ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
- ieee80211_8023_xmit(sdata, dev, sta, key, skb);
- goto out;
-
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
-@@ -625,21 +625,6 @@ static int wiphy_verify_combinations(str
+@@ -614,21 +614,6 @@ static int wiphy_verify_combinations(str
c->limits[j].max > 1))
return -EINVAL;
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
-@@ -3869,6 +3869,7 @@ struct mgmt_frame_regs {
+@@ -4081,6 +4081,7 @@ struct mgmt_frame_regs {
* (as advertised by the nl80211 feature flag.)
* @get_tx_power: store the current TX power into the dbm variable;
* return 0 if successful
*
* @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
* functions to adjust rfkill hw state
-@@ -4202,6 +4203,7 @@ struct cfg80211_ops {
+@@ -4431,6 +4432,7 @@ struct cfg80211_ops {
enum nl80211_tx_power_setting type, int mbm);
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
int *dbm);
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
-@@ -1566,6 +1566,7 @@ enum ieee80211_smps_mode {
+@@ -1645,6 +1645,7 @@ enum ieee80211_smps_mode {
*
* @power_level: requested transmit power (in dBm), backward compatibility
* value only that is set to the minimum of all interfaces
*
* @chandef: the channel definition to tune to
* @radar_enabled: whether radar detection is enabled
-@@ -1586,6 +1587,7 @@ enum ieee80211_smps_mode {
+@@ -1665,6 +1666,7 @@ enum ieee80211_smps_mode {
struct ieee80211_conf {
u32 flags;
int power_level, dynamic_ps_timeout;
u8 ps_dtim_period;
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
-@@ -2615,6 +2615,9 @@ enum nl80211_commands {
- * switching on a different channel during CAC detection on the selected
- * radar channel.
- *
+@@ -2749,6 +2749,9 @@ enum nl80211_commands {
+ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
+ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ * the incoming frame RX timestamp.
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+ * transmit power to stay within regulatory limits. u32, dBi.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
-@@ -3123,6 +3126,8 @@ enum nl80211_attrs {
-
- NL80211_ATTR_RADAR_BACKGROUND,
+@@ -3277,6 +3280,8 @@ enum nl80211_attrs {
+ NL80211_ATTR_TX_HW_TIMESTAMP,
+ NL80211_ATTR_RX_HW_TIMESTAMP,
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+
__NL80211_ATTR_AFTER_LAST,
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
-@@ -2812,6 +2812,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2998,6 +2998,19 @@ static int ieee80211_get_tx_power(struct
return 0;
}
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
-@@ -4513,6 +4526,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4881,6 +4894,7 @@ const struct cfg80211_ops mac80211_confi
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
-@@ -1447,6 +1447,7 @@ struct ieee80211_local {
+@@ -1521,6 +1521,7 @@ struct ieee80211_local {
int dynamic_ps_forced_timeout;
int user_power_level; /* in dBm, for all interfaces */
if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER;
local->hw.conf.power_level = power;
-@@ -679,6 +685,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+@@ -762,6 +768,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
IEEE80211_RADIOTAP_MCS_HAVE_BW;
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
local->hw.max_mtu = IEEE80211_MAX_DATA_LEN;
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
-@@ -802,6 +802,7 @@ static const struct nla_policy nl80211_p
- NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
- [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
- [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
+@@ -799,6 +799,7 @@ static const struct nla_policy nl80211_p
+ [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN),
+ [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
};
/* policy for the key attributes */
-@@ -3391,6 +3392,22 @@ static int nl80211_set_wiphy(struct sk_b
+@@ -3511,6 +3512,22 @@ static int nl80211_set_wiphy(struct sk_b
if (result)
goto out;
}
+++ /dev/null
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -6027,6 +6027,11 @@ enum nl80211_feature_flags {
- * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
- * detection and change announcemnts.
- *
-+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
-+ * FILS encryption and decryption for (Re)Association Request and Response
-+ * frames. Userspace has to share FILS AAD details to the driver by using
-+ * @NL80211_CMD_SET_FILS_AAD.
-+ *
- * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
- * detection.
- *
-@@ -6095,6 +6100,7 @@ enum nl80211_ext_feature_index {
- NL80211_EXT_FEATURE_SECURE_RTT,
- NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
- NL80211_EXT_FEATURE_BSS_COLOR,
-+ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
- NL80211_EXT_FEATURE_RADAR_BACKGROUND,
-
- /* add new features before the definition below */
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
-@@ -251,7 +251,11 @@ static void ieee80211_send_addba_resp(st
+@@ -254,7 +254,11 @@ static void ieee80211_send_addba_resp(st
mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
--- /dev/null
+From ec46d7486ab91786fbfe77edd306911b7c543334 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 11 Oct 2022 17:58:31 +0200
+Subject: [PATCH] sync with upstream
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ debugfs.c | 28 ++++++++
+ dma.c | 2 +-
+ eeprom.c | 1 -
+ mac80211.c | 4 +-
+ mt7603/beacon.c | 2 +-
+ mt7603/dma.c | 4 +-
+ mt7603/mac.c | 16 +++--
+ mt7603/main.c | 9 +--
+ mt7615/dma.c | 4 +-
+ mt7615/main.c | 7 +-
+ mt7615/mcu.c | 8 +--
+ mt7615/soc.c | 3 +-
+ mt76_connac_mcu.c | 101 ++++++++++++++--------------
+ mt76x02.h | 5 +-
+ mt76x02_beacon.c | 2 +-
+ mt76x02_mac.c | 6 +-
+ mt76x02_mmio.c | 4 +-
+ mt76x02_util.c | 5 +-
+ mt76x2/pci.c | 2 +-
+ mt7915/Kconfig | 1 +
+ mt7915/debugfs.c | 4 +-
+ mt7915/dma.c | 4 +-
+ mt7915/init.c | 3 +-
+ mt7915/mac.c | 2 +-
+ mt7915/main.c | 11 +--
+ mt7915/mcu.c | 166 +++++++++++++++++++++++-----------------------
+ mt7915/soc.c | 1 +
+ mt7921/dma.c | 4 +-
+ mt7921/init.c | 4 +-
+ mt7921/mac.c | 2 +-
+ mt7921/main.c | 18 ++---
+ mt7921/mcu.c | 6 +-
+ mt7921/pci_mac.c | 2 +-
+ tx.c | 11 ++-
+ usb.c | 2 +-
+ 35 files changed, 250 insertions(+), 204 deletions(-)
+
+diff --git a/debugfs.c b/debugfs.c
+index b7aa8727ad1e..11b8153544a0 100644
+--- a/debugfs.c
++++ b/debugfs.c
+@@ -25,6 +25,32 @@ mt76_reg_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set,
+ "0x%08llx\n");
+
++static int
++mt76_napi_threaded_set(void *data, u64 val)
++{
++ struct mt76_dev *dev = data;
++
++ if (!mt76_is_mmio(dev))
++ return -EOPNOTSUPP;
++
++ if (dev->napi_dev.threaded != val)
++ return dev_set_threaded(&dev->napi_dev, val);
++
++ return 0;
++}
++
++static int
++mt76_napi_threaded_get(void *data, u64 *val)
++{
++ struct mt76_dev *dev = data;
++
++ *val = dev->napi_dev.threaded;
++ return 0;
++}
++
++DEFINE_DEBUGFS_ATTRIBUTE(fops_napi_threaded, mt76_napi_threaded_get,
++ mt76_napi_threaded_set, "%llu\n");
++
+ int mt76_queues_read(struct seq_file *s, void *data)
+ {
+ struct mt76_dev *dev = dev_get_drvdata(s->private);
+@@ -90,6 +116,8 @@ mt76_register_debugfs_fops(struct mt76_phy *phy,
+ debugfs_create_bool("led_active_low", 0600, dir, &dev->led_al);
+ debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg);
+ debugfs_create_file_unsafe("regval", 0600, dir, dev, fops);
++ debugfs_create_file_unsafe("napi_threaded", 0600, dir, dev,
++ &fops_napi_threaded);
+ debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom);
+ if (dev->otp.data)
+ debugfs_create_blob("otp", 0400, dir, &dev->otp);
+diff --git a/dma.c b/dma.c
+index ae22b9592ead..8dca8d2447b7 100644
+--- a/dma.c
++++ b/dma.c
+@@ -895,7 +895,7 @@ mt76_dma_init(struct mt76_dev *dev,
+ dev->napi_dev.threaded = 1;
+
+ mt76_for_each_q_rx(dev, i) {
+- netif_napi_add(&dev->napi_dev, &dev->napi[i], poll, 64);
++ netif_napi_add(&dev->napi_dev, &dev->napi[i], poll);
+ mt76_dma_rx_fill(dev, &dev->q_rx[i]);
+ napi_enable(&dev->napi[i]);
+ }
+diff --git a/eeprom.c b/eeprom.c
+index 25f677601034..0a88048b8976 100644
+--- a/eeprom.c
++++ b/eeprom.c
+@@ -106,7 +106,6 @@ void
+ mt76_eeprom_override(struct mt76_phy *phy)
+ {
+ struct mt76_dev *dev = phy->dev;
+-
+ struct device_node *np = dev->dev->of_node;
+
+ of_get_mac_address(np, phy->macaddr);
+diff --git a/mac80211.c b/mac80211.c
+index acac04eff0d6..fc608b369b3c 100644
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -1482,7 +1482,7 @@ EXPORT_SYMBOL_GPL(mt76_get_sar_power);
+ static void
+ __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (vif->csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
++ if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
+ ieee80211_csa_finish(vif);
+ }
+
+@@ -1504,7 +1504,7 @@ __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+ struct mt76_dev *dev = priv;
+
+- if (!vif->csa_active)
++ if (!vif->bss_conf.csa_active)
+ return;
+
+ dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
+diff --git a/mt7603/beacon.c b/mt7603/beacon.c
+index 5807cf2cf862..b65b0a88c1de 100644
+--- a/mt7603/beacon.c
++++ b/mt7603/beacon.c
+@@ -20,7 +20,7 @@ mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ if (!(mdev->beacon_mask & BIT(mvif->idx)))
+ return;
+
+- skb = ieee80211_beacon_get(mt76_hw(dev), vif);
++ skb = ieee80211_beacon_get(mt76_hw(dev), vif, 0);
+ if (!skb)
+ return;
+
+diff --git a/mt7603/dma.c b/mt7603/dma.c
+index 06a9e6ecce60..03ba11a61c90 100644
+--- a/mt7603/dma.c
++++ b/mt7603/dma.c
+@@ -223,8 +223,8 @@ int mt7603_dma_init(struct mt7603_dev *dev)
+ if (ret)
+ return ret;
+
+- netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt7603_poll_tx, NAPI_POLL_WEIGHT);
++ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt7603_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ return 0;
+diff --git a/mt7603/mac.c b/mt7603/mac.c
+index b025e3553006..70a7f84af028 100644
+--- a/mt7603/mac.c
++++ b/mt7603/mac.c
+@@ -326,19 +326,21 @@ void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta)
+
+ addr = mt7603_wtbl1_addr(idx);
+
+- ampdu_density = sta->ht_cap.ampdu_density;
++ ampdu_density = sta->deflink.ht_cap.ampdu_density;
+ if (ampdu_density < IEEE80211_HT_MPDU_DENSITY_4)
+ ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+
+ val = mt76_rr(dev, addr + 2 * 4);
+ val &= MT_WTBL1_W2_KEY_TYPE | MT_WTBL1_W2_ADMISSION_CONTROL;
+- val |= FIELD_PREP(MT_WTBL1_W2_AMPDU_FACTOR, sta->ht_cap.ampdu_factor) |
+- FIELD_PREP(MT_WTBL1_W2_MPDU_DENSITY, sta->ht_cap.ampdu_density) |
++ val |= FIELD_PREP(MT_WTBL1_W2_AMPDU_FACTOR,
++ sta->deflink.ht_cap.ampdu_factor) |
++ FIELD_PREP(MT_WTBL1_W2_MPDU_DENSITY,
++ sta->deflink.ht_cap.ampdu_density) |
+ MT_WTBL1_W2_TXS_BAF_REPORT;
+
+- if (sta->ht_cap.cap)
++ if (sta->deflink.ht_cap.cap)
+ val |= MT_WTBL1_W2_HT;
+- if (sta->vht_cap.cap)
++ if (sta->deflink.vht_cap.cap)
+ val |= MT_WTBL1_W2_VHT;
+
+ mt76_wr(dev, addr + 2 * 4, val);
+@@ -347,9 +349,9 @@ void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta)
+ val = mt76_rr(dev, addr + 9 * 4);
+ val &= ~(MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 |
+ MT_WTBL2_W9_SHORT_GI_80);
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ val |= MT_WTBL2_W9_SHORT_GI_20;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ val |= MT_WTBL2_W9_SHORT_GI_40;
+ mt76_wr(dev, addr + 9 * 4, val);
+ }
+diff --git a/mt7603/main.c b/mt7603/main.c
+index 91425b454cae..ca50feb0b3a9 100644
+--- a/mt7603/main.c
++++ b/mt7603/main.c
+@@ -297,7 +297,7 @@ mt7603_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
+
+ static void
+ mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- struct ieee80211_bss_conf *info, u32 changed)
++ struct ieee80211_bss_conf *info, u64 changed)
+ {
+ struct mt7603_dev *dev = hw->priv;
+ struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;
+@@ -305,7 +305,7 @@ mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mutex_lock(&dev->mt76.mutex);
+
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID)) {
+- if (info->assoc || info->ibss_joined) {
++ if (vif->cfg.assoc || vif->cfg.ibss_joined) {
+ mt76_wr(dev, MT_BSSID0(mvif->idx),
+ get_unaligned_le32(info->bssid));
+ mt76_wr(dev, MT_BSSID1(mvif->idx),
+@@ -527,7 +527,8 @@ mt7603_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ }
+
+ static int
+-mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
++mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mt7603_dev *dev = hw->priv;
+@@ -657,7 +658,7 @@ mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ mt7603_wtbl_set_rates(dev, msta, NULL, msta->rates);
+ msta->rate_probe = false;
+ mt7603_wtbl_set_smps(dev, msta,
+- sta->smps_mode == IEEE80211_SMPS_DYNAMIC);
++ sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC);
+ spin_unlock_bh(&dev->mt76.lock);
+ }
+
+diff --git a/mt7615/dma.c b/mt7615/dma.c
+index ec729dbe1cd8..f1914431ff7f 100644
+--- a/mt7615/dma.c
++++ b/mt7615/dma.c
+@@ -281,8 +281,8 @@ int mt7615_dma_init(struct mt7615_dev *dev)
+ if (ret < 0)
+ return ret;
+
+- netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt7615_poll_tx, NAPI_POLL_WEIGHT);
++ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt7615_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ mt76_poll(dev, MT_WPDMA_GLO_CFG,
+diff --git a/mt7615/main.c b/mt7615/main.c
+index 0d80003c6f17..ab4c1b4478aa 100644
+--- a/mt7615/main.c
++++ b/mt7615/main.c
+@@ -473,7 +473,8 @@ static int mt7615_config(struct ieee80211_hw *hw, u32 changed)
+ }
+
+ static int
+-mt7615_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
++mt7615_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+@@ -555,7 +556,7 @@ static void mt7615_configure_filter(struct ieee80211_hw *hw,
+ static void mt7615_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct mt7615_dev *dev = mt7615_hw_dev(hw);
+ struct mt7615_phy *phy = mt7615_hw_phy(hw);
+@@ -598,7 +599,7 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw,
+ }
+
+ if (changed & BSS_CHANGED_ASSOC)
+- mt7615_mac_set_beacon_filter(phy, vif, info->assoc);
++ mt7615_mac_set_beacon_filter(phy, vif, vif->cfg.assoc);
+
+ mt7615_mutex_release(dev);
+ }
+diff --git a/mt7615/mcu.c b/mt7615/mcu.c
+index 81579a1422d4..83f30305414d 100644
+--- a/mt7615/mcu.c
++++ b/mt7615/mcu.c
+@@ -352,7 +352,7 @@ out:
+ static void
+ mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (vif->csa_active)
++ if (vif->bss_conf.csa_active)
+ ieee80211_csa_finish(vif);
+ }
+
+@@ -698,7 +698,7 @@ mt7615_mcu_add_beacon_offload(struct mt7615_dev *dev,
+ if (!enable)
+ goto out;
+
+- skb = ieee80211_beacon_get_template(hw, vif, &offs);
++ skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+ if (!skb)
+ return -EINVAL;
+
+@@ -1073,7 +1073,7 @@ mt7615_mcu_uni_add_beacon_offload(struct mt7615_dev *dev,
+ if (!enable)
+ goto out;
+
+- skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs);
++ skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0);
+ if (!skb)
+ return -EINVAL;
+
+@@ -2525,7 +2525,7 @@ int mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif,
+ u8 pad;
+ } req = {
+ .bss_idx = mvif->mt76.idx,
+- .aid = cpu_to_le16(vif->bss_conf.aid),
++ .aid = cpu_to_le16(vif->cfg.aid),
+ .dtim_period = vif->bss_conf.dtim_period,
+ .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
+ };
+diff --git a/mt7615/soc.c b/mt7615/soc.c
+index be9a69fe1b38..f13d1b418742 100644
+--- a/mt7615/soc.c
++++ b/mt7615/soc.c
+@@ -31,7 +31,6 @@ int mt7622_wmac_init(struct mt7615_dev *dev)
+
+ static int mt7622_wmac_probe(struct platform_device *pdev)
+ {
+- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ void __iomem *mem_base;
+ int irq;
+
+@@ -39,7 +38,7 @@ static int mt7622_wmac_probe(struct platform_device *pdev)
+ if (irq < 0)
+ return irq;
+
+- mem_base = devm_ioremap_resource(&pdev->dev, res);
++ mem_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(mem_base))
+ return PTR_ERR(mem_base);
+
+diff --git a/mt76_connac_mcu.c b/mt76_connac_mcu.c
+index 673fca296b89..83c59d2de692 100644
+--- a/mt76_connac_mcu.c
++++ b/mt76_connac_mcu.c
+@@ -196,7 +196,7 @@ int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif)
+ */
+ } req = {
+ .bss_idx = mvif->idx,
+- .ps_state = vif->bss_conf.ps ? 2 : 0,
++ .ps_state = vif->cfg.ps ? 2 : 0,
+ };
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+@@ -407,7 +407,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct sk_buff *skb,
+ else
+ conn_type = CONNECTION_INFRA_AP;
+ basic->conn_type = cpu_to_le32(conn_type);
+- basic->aid = cpu_to_le16(vif->bss_conf.aid);
++ basic->aid = cpu_to_le16(vif->cfg.aid);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC);
+@@ -551,7 +551,7 @@ void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev,
+
+ if (sta) {
+ if (vif->type == NL80211_IFTYPE_STATION)
+- generic->partial_aid = cpu_to_le16(vif->bss_conf.aid);
++ generic->partial_aid = cpu_to_le16(vif->cfg.aid);
+ else
+ generic->partial_aid = cpu_to_le16(sta->aid);
+ memcpy(generic->peer_addr, sta->addr, ETH_ALEN);
+@@ -597,14 +597,14 @@ mt76_connac_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
+ vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+- if (!sta->max_amsdu_len)
++ if (!sta->deflink.agg.max_amsdu_len)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
+ amsdu = (struct sta_rec_amsdu *)tlv;
+ amsdu->max_amsdu_num = 8;
+ amsdu->amsdu_en = true;
+- amsdu->max_mpdu_size = sta->max_amsdu_len >=
++ amsdu->max_mpdu_size = sta->deflink.agg.max_amsdu_len >=
+ IEEE80211_MAX_MPDU_LEN_VHT_7991;
+
+ wcid->amsdu = true;
+@@ -615,7 +615,7 @@ mt76_connac_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
+ static void
+ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ {
+- struct ieee80211_sta_he_cap *he_cap = &sta->he_cap;
++ struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
+ struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
+ struct sta_rec_he *he;
+ struct tlv *tlv;
+@@ -703,7 +703,7 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+
+ he->he_cap = cpu_to_le32(cap);
+
+- switch (sta->bandwidth) {
++ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+@@ -755,9 +755,9 @@ mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ u8 mode = 0;
+
+ if (sta) {
+- ht_cap = &sta->ht_cap;
+- vht_cap = &sta->vht_cap;
+- he_cap = &sta->he_cap;
++ ht_cap = &sta->deflink.ht_cap;
++ vht_cap = &sta->deflink.vht_cap;
++ he_cap = &sta->deflink.he_cap;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+@@ -806,25 +806,25 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ u16 supp_rates;
+
+ /* starec ht */
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ struct sta_rec_ht *ht;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+ ht = (struct sta_rec_ht *)tlv;
+- ht->ht_cap = cpu_to_le16(sta->ht_cap.cap);
++ ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+ }
+
+ /* starec vht */
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ struct sta_rec_vht *vht;
+ int len;
+
+ len = is_mt7921(dev) ? sizeof(*vht) : sizeof(*vht) - 4;
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, len);
+ vht = (struct sta_rec_vht *)tlv;
+- vht->vht_cap = cpu_to_le32(sta->vht_cap.cap);
+- vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map;
+- vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map;
++ vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
++ vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
++ vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
+ }
+
+ /* starec uapsd */
+@@ -833,11 +833,11 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ if (!is_mt7921(dev))
+ return;
+
+- if (sta->ht_cap.ht_supported || sta->he_cap.has_he)
++ if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)
+ mt76_connac_mcu_sta_amsdu_tlv(skb, sta, vif);
+
+ /* starec he */
+- if (sta->he_cap.has_he) {
++ if (sta->deflink.he_cap.has_he) {
+ mt76_connac_mcu_sta_he_tlv(skb, sta);
+ if (band == NL80211_BAND_6GHZ &&
+ sta_state == MT76_STA_INFO_STATE_ASSOC) {
+@@ -846,7 +846,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G,
+ sizeof(*he_6g_capa));
+ he_6g_capa = (struct sta_rec_he_6g_capa *)tlv;
+- he_6g_capa->capa = sta->he_6ghz_capa.capa;
++ he_6g_capa->capa = sta->deflink.he_6ghz_capa.capa;
+ }
+ }
+
+@@ -856,14 +856,14 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+ phy->basic_rate = cpu_to_le16((u16)vif->bss_conf.basic_rates);
+ phy->rcpi = rcpi;
+ phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR,
+- sta->ht_cap.ampdu_factor) |
++ sta->deflink.ht_cap.ampdu_factor) |
+ FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY,
+- sta->ht_cap.ampdu_density);
++ sta->deflink.ht_cap.ampdu_density);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info));
+ ra_info = (struct sta_rec_ra_info *)tlv;
+
+- supp_rates = sta->supp_rates[band];
++ supp_rates = sta->deflink.supp_rates[band];
+ if (band == NL80211_BAND_2GHZ)
+ supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) |
+ FIELD_PREP(RA_LEGACY_CCK, supp_rates & 0xf);
+@@ -872,17 +872,18 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
+
+ ra_info->legacy = cpu_to_le16(supp_rates);
+
+- if (sta->ht_cap.ht_supported)
+- memcpy(ra_info->rx_mcs_bitmask, sta->ht_cap.mcs.rx_mask,
++ if (sta->deflink.ht_cap.ht_supported)
++ memcpy(ra_info->rx_mcs_bitmask,
++ sta->deflink.ht_cap.mcs.rx_mask,
+ HT_MCS_MASK_NUM);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state));
+ state = (struct sta_rec_state *)tlv;
+ state->state = sta_state;
+
+- if (sta->vht_cap.vht_supported) {
+- state->vht_opmode = sta->bandwidth;
+- state->vht_opmode |= (sta->rx_nss - 1) <<
++ if (sta->deflink.vht_cap.vht_supported) {
++ state->vht_opmode = sta->deflink.bandwidth;
++ state->vht_opmode |= (sta->deflink.rx_nss - 1) <<
+ IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
+ }
+ }
+@@ -898,7 +899,7 @@ void mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb,
+ tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_SMPS, sizeof(*smps),
+ wtbl_tlv, sta_wtbl);
+ smps = (struct wtbl_smps *)tlv;
+- smps->smps = (sta->smps_mode == IEEE80211_SMPS_DYNAMIC);
++ smps->smps = (sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC);
+ }
+ EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_smps_tlv);
+
+@@ -910,27 +911,27 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ struct tlv *tlv;
+ u32 flags = 0;
+
+- if (sta->ht_cap.ht_supported || sta->he_6ghz_capa.capa) {
++ if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_6ghz_capa.capa) {
+ tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_HT, sizeof(*ht),
+ wtbl_tlv, sta_wtbl);
+ ht = (struct wtbl_ht *)tlv;
+ ht->ldpc = ht_ldpc &&
+- !!(sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING);
++ !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING);
+
+- if (sta->ht_cap.ht_supported) {
+- ht->af = sta->ht_cap.ampdu_factor;
+- ht->mm = sta->ht_cap.ampdu_density;
++ if (sta->deflink.ht_cap.ht_supported) {
++ ht->af = sta->deflink.ht_cap.ampdu_factor;
++ ht->mm = sta->deflink.ht_cap.ampdu_density;
+ } else {
+- ht->af = le16_get_bits(sta->he_6ghz_capa.capa,
++ ht->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+- ht->mm = le16_get_bits(sta->he_6ghz_capa.capa,
++ ht->mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
+ }
+
+ ht->ht = true;
+ }
+
+- if (sta->vht_cap.vht_supported || sta->he_6ghz_capa.capa) {
++ if (sta->deflink.vht_cap.vht_supported || sta->deflink.he_6ghz_capa.capa) {
+ struct wtbl_vht *vht;
+ u8 af;
+
+@@ -939,18 +940,18 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ sta_wtbl);
+ vht = (struct wtbl_vht *)tlv;
+ vht->ldpc = vht_ldpc &&
+- !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC);
++ !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC);
+ vht->vht = true;
+
+ af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+- sta->vht_cap.cap);
++ sta->deflink.vht_cap.cap);
+ if (ht)
+ ht->af = max(ht->af, af);
+ }
+
+ mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_tlv);
+
+- if (is_connac_v1(dev) && sta->ht_cap.ht_supported) {
++ if (is_connac_v1(dev) && sta->deflink.ht_cap.ht_supported) {
+ /* sgi */
+ u32 msk = MT_WTBL_W5_SHORT_GI_20 | MT_WTBL_W5_SHORT_GI_40 |
+ MT_WTBL_W5_SHORT_GI_80 | MT_WTBL_W5_SHORT_GI_160;
+@@ -960,15 +961,15 @@ void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb,
+ sizeof(*raw), wtbl_tlv,
+ sta_wtbl);
+
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ flags |= MT_WTBL_W5_SHORT_GI_20;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ flags |= MT_WTBL_W5_SHORT_GI_40;
+
+- if (sta->vht_cap.vht_supported) {
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
++ if (sta->deflink.vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ flags |= MT_WTBL_W5_SHORT_GI_80;
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ flags |= MT_WTBL_W5_SHORT_GI_160;
+ }
+ raw = (struct wtbl_raw *)tlv;
+@@ -1254,9 +1255,9 @@ u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif,
+ return 0x38;
+
+ if (sta) {
+- ht_cap = &sta->ht_cap;
+- vht_cap = &sta->vht_cap;
+- he_cap = &sta->he_cap;
++ ht_cap = &sta->deflink.ht_cap;
++ vht_cap = &sta->deflink.vht_cap;
++ he_cap = &sta->deflink.he_cap;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+@@ -2196,8 +2197,10 @@ int mt76_connac_mcu_update_arp_filter(struct mt76_dev *dev,
+ struct mt76_vif *vif,
+ struct ieee80211_bss_conf *info)
+ {
++ struct ieee80211_vif *mvif = container_of(info, struct ieee80211_vif,
++ bss_conf);
+ struct sk_buff *skb;
+- int i, len = min_t(int, info->arp_addr_cnt,
++ int i, len = min_t(int, mvif->cfg.arp_addr_cnt,
+ IEEE80211_BSS_ARP_ADDR_LIST_LEN);
+ struct {
+ struct {
+@@ -2225,7 +2228,7 @@ int mt76_connac_mcu_update_arp_filter(struct mt76_dev *dev,
+
+ skb_put_data(skb, &req_hdr, sizeof(req_hdr));
+ for (i = 0; i < len; i++)
+- skb_put_data(skb, &info->arp_addr_list[i], sizeof(__be32));
++ skb_put_data(skb, &mvif->cfg.arp_addr_list[i], sizeof(__be32));
+
+ return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true);
+ }
+diff --git a/mt76x02.h b/mt76x02.h
+index 3f2a9b7fa070..4cd63bacd742 100644
+--- a/mt76x02.h
++++ b/mt76x02.h
+@@ -170,7 +170,8 @@ int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+ int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- u16 queue, const struct ieee80211_tx_queue_params *params);
++ unsigned int link_id, u16 queue,
++ const struct ieee80211_tx_queue_params *params);
+ void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+@@ -201,7 +202,7 @@ void mt76x02_sw_scan_complete(struct ieee80211_hw *hw,
+ void mt76x02_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);
+ void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+- struct ieee80211_bss_conf *info, u32 changed);
++ struct ieee80211_bss_conf *info, u64 changed);
+ void mt76x02_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type);
+
+diff --git a/mt76x02_beacon.c b/mt76x02_beacon.c
+index 67844e2ed3f8..ad4dc8e17b58 100644
+--- a/mt76x02_beacon.c
++++ b/mt76x02_beacon.c
+@@ -143,7 +143,7 @@ mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ if (!(dev->mt76.beacon_mask & BIT(mvif->idx)))
+ return;
+
+- skb = ieee80211_beacon_get(mt76_hw(dev), vif);
++ skb = ieee80211_beacon_get(mt76_hw(dev), vif, 0);
+ if (!skb)
+ return;
+
+diff --git a/mt76x02_mac.c b/mt76x02_mac.c
+index 87ea3db140d9..d3f74473e6fb 100644
+--- a/mt76x02_mac.c
++++ b/mt76x02_mac.c
+@@ -404,7 +404,7 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
+ txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC);
+ if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1)
+ txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC);
+- if (nss > 1 && sta && sta->smps_mode == IEEE80211_SMPS_DYNAMIC)
++ if (nss > 1 && sta && sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC)
+ txwi_flags |= MT_TXWI_FLAGS_MMPS;
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+ txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ;
+@@ -412,9 +412,9 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi,
+ txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ;
+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) {
+ u8 ba_size = IEEE80211_MIN_AMPDU_BUF;
+- u8 ampdu_density = sta->ht_cap.ampdu_density;
++ u8 ampdu_density = sta->deflink.ht_cap.ampdu_density;
+
+- ba_size <<= sta->ht_cap.ampdu_factor;
++ ba_size <<= sta->deflink.ht_cap.ampdu_factor;
+ ba_size = min_t(int, 63, ba_size - 1);
+ if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+ ba_size = 0;
+diff --git a/mt76x02_mmio.c b/mt76x02_mmio.c
+index 648f4ab9d684..e9c5e85ec07c 100644
+--- a/mt76x02_mmio.c
++++ b/mt76x02_mmio.c
+@@ -231,8 +231,8 @@ int mt76x02_dma_init(struct mt76x02_dev *dev)
+ if (ret)
+ return ret;
+
+- netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt76x02_poll_tx, NAPI_POLL_WEIGHT);
++ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt76x02_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ return 0;
+diff --git a/mt76x02_util.c b/mt76x02_util.c
+index 5bd0a0bae688..604ddcc21123 100644
+--- a/mt76x02_util.c
++++ b/mt76x02_util.c
+@@ -487,7 +487,8 @@ int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ EXPORT_SYMBOL_GPL(mt76x02_set_key);
+
+ int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+- u16 queue, const struct ieee80211_tx_queue_params *params)
++ unsigned int link_id, u16 queue,
++ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mt76x02_dev *dev = hw->priv;
+ u8 cw_min = 5, cw_max = 10, qid;
+@@ -636,7 +637,7 @@ EXPORT_SYMBOL_GPL(mt76x02_sta_ps);
+ void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+ struct mt76x02_dev *dev = hw->priv;
+diff --git a/mt76x2/pci.c b/mt76x2/pci.c
+index 8a22ee581674..df85ebc6e1df 100644
+--- a/mt76x2/pci.c
++++ b/mt76x2/pci.c
+@@ -80,7 +80,7 @@ mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9);
+
+ /* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */
+- mt76_rmw_field(dev, 0x15a0c, 0xf << 28, 0xf);
++ mt76_rmw_field(dev, 0x15a0c, 0xfU << 28, 0xf);
+
+ /* RG_SSUSB_CDR_BR_PE1D = 0x3 */
+ mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3);
+diff --git a/mt7915/Kconfig b/mt7915/Kconfig
+index 320012543634..d710726d47bf 100644
+--- a/mt7915/Kconfig
++++ b/mt7915/Kconfig
+@@ -5,6 +5,7 @@ config MT7915E
+ select WANT_DEV_COREDUMP
+ depends on MAC80211
+ depends on PCI
++ select RELAY
+ help
+ This adds support for MT7915-based wireless PCIe devices,
+ which support concurrent dual-band operation at both 5GHz
+diff --git a/mt7915/debugfs.c b/mt7915/debugfs.c
+index e7c61378d1f0..fb46c2c1784f 100644
+--- a/mt7915/debugfs.c
++++ b/mt7915/debugfs.c
+@@ -1362,8 +1362,8 @@ static ssize_t mt7915_sta_fixed_rate_set(struct file *file,
+
+ phy.ldpc = (phy.bw || phy.ldpc) * GENMASK(2, 0);
+ for (i = 0; i <= phy.bw; i++) {
+- phy.sgi |= gi << (i << sta->he_cap.has_he);
+- phy.he_ltf |= he_ltf << (i << sta->he_cap.has_he);
++ phy.sgi |= gi << (i << sta->deflink.he_cap.has_he);
++ phy.he_ltf |= he_ltf << (i << sta->deflink.he_cap.has_he);
+ }
+ field = RATE_PARAM_FIXED;
+
+diff --git a/mt7915/dma.c b/mt7915/dma.c
+index 0ca68d4a999a..e3fa064918bf 100644
+--- a/mt7915/dma.c
++++ b/mt7915/dma.c
+@@ -550,8 +550,8 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
+ if (ret < 0)
+ return ret;
+
+- netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt7915_poll_tx, NAPI_POLL_WEIGHT);
++ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt7915_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ mt7915_dma_enable(dev);
+diff --git a/mt7915/init.c b/mt7915/init.c
+index dfc3f53b1676..b97eb2cc8e8f 100644
+--- a/mt7915/init.c
++++ b/mt7915/init.c
+@@ -1022,7 +1022,8 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
+ mt7915_gen_ppe_thresh(he_cap->ppe_thres, nss);
+ } else {
+ he_cap_elem->phy_cap_info[9] |=
+- IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US;
++ u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
++ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+ }
+
+ if (band == NL80211_BAND_6GHZ) {
+diff --git a/mt7915/mac.c b/mt7915/mac.c
+index b477e9d9fe03..f0d5a3603902 100644
+--- a/mt7915/mac.c
++++ b/mt7915/mac.c
+@@ -853,7 +853,7 @@ mt7915_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ u16 fc, tid;
+ u32 val;
+
+- if (!sta || !(sta->ht_cap.ht_supported || sta->he_cap.has_he))
++ if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+diff --git a/mt7915/main.c b/mt7915/main.c
+index 076688d8e7f0..0511d6a505b0 100644
+--- a/mt7915/main.c
++++ b/mt7915/main.c
+@@ -490,7 +490,8 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
+ }
+
+ static int
+-mt7915_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
++mt7915_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+@@ -585,7 +586,7 @@ mt7915_update_bss_color(struct ieee80211_hw *hw,
+ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct mt7915_phy *phy = mt7915_hw_phy(hw);
+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+@@ -605,7 +606,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
+ }
+
+ if (changed & BSS_CHANGED_ASSOC)
+- mt7915_mcu_add_bss_info(phy, vif, info->assoc);
++ mt7915_mcu_add_bss_info(phy, vif, vif->cfg.assoc);
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot);
+@@ -1145,10 +1146,10 @@ static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
+ {
+ struct mt7915_phy *phy = mt7915_hw_phy(hw);
+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+- s16 txpower = sta->txpwr.power;
++ s16 txpower = sta->deflink.txpwr.power;
+ int ret;
+
+- if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC)
++ if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC)
+ txpower = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+diff --git a/mt7915/mcu.c b/mt7915/mcu.c
+index b787fa0cd307..d7d84b0f863f 100644
+--- a/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -59,7 +59,7 @@ mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+ struct mt7915_dev *dev = msta->vif->phy->dev;
+ enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
+ const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
++ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++) {
+ int mcs;
+@@ -99,7 +99,7 @@ mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+
+ /* only support 2ss on 160MHz for mt7915 */
+ if (is_mt7915(&dev->mt76) && nss > 1 &&
+- sta->bandwidth == IEEE80211_STA_RX_BW_160)
++ sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ break;
+ }
+
+@@ -112,8 +112,8 @@ mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+ {
+ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+ struct mt7915_dev *dev = msta->vif->phy->dev;
+- u16 mcs_map = le16_to_cpu(sta->vht_cap.vht_mcs.rx_mcs_map);
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
++ u16 mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
++ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+ u16 mcs;
+
+ for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
+@@ -135,7 +135,7 @@ mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
+
+ /* only support 2ss on 160MHz for mt7915 */
+ if (is_mt7915(&dev->mt76) && nss > 1 &&
+- sta->bandwidth == IEEE80211_STA_RX_BW_160)
++ sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ break;
+ }
+ }
+@@ -144,10 +144,10 @@ static void
+ mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
+ const u8 *mask)
+ {
+- int nss, max_nss = sta->rx_nss > 3 ? 4 : sta->rx_nss;
++ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++)
+- ht_mcs[nss] = sta->ht_cap.mcs.rx_mask[nss] & mask[nss];
++ ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
+ }
+
+ static int
+@@ -220,7 +220,7 @@ int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
+ static void
+ mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (vif->csa_active)
++ if (vif->bss_conf.csa_active)
+ ieee80211_csa_finish(vif);
+ }
+
+@@ -312,7 +312,7 @@ mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb)
+ static void
+ mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+ {
+- if (!vif->color_change_active)
++ if (!vif->bss_conf.color_change_active)
+ return;
+
+ ieee80211_color_change_finish(vif);
+@@ -694,13 +694,13 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif)
+ {
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct ieee80211_he_cap_elem *elem = &sta->he_cap.he_cap_elem;
++ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp mcs_map;
+ struct sta_rec_he *he;
+ struct tlv *tlv;
+ u32 cap = 0;
+
+- if (!sta->he_cap.has_he)
++ if (!sta->deflink.he_cap.has_he)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he));
+@@ -786,8 +786,8 @@ mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
+
+ he->he_cap = cpu_to_le32(cap);
+
+- mcs_map = sta->he_cap.he_mcs_nss_supp;
+- switch (sta->bandwidth) {
++ mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
++ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+@@ -837,7 +837,7 @@ mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ struct ieee80211_sta *sta, struct ieee80211_vif *vif)
+ {
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+- struct ieee80211_he_cap_elem *elem = &sta->he_cap.he_cap_elem;
++ struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
+ struct sta_rec_muru *muru;
+ struct tlv *tlv;
+
+@@ -856,11 +856,11 @@ mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ muru->cfg.mimo_ul_en = true;
+ muru->cfg.ofdma_dl_en = true;
+
+- if (sta->vht_cap.vht_supported)
++ if (sta->deflink.vht_cap.vht_supported)
+ muru->mimo_dl.vht_mu_bfee =
+- !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
++ !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+
+- if (!sta->he_cap.has_he)
++ if (!sta->deflink.he_cap.has_he)
+ return;
+
+ muru->mimo_dl.partial_bw_dl_mimo =
+@@ -894,13 +894,13 @@ mt7915_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ struct sta_rec_ht *ht;
+ struct tlv *tlv;
+
+- if (!sta->ht_cap.ht_supported)
++ if (!sta->deflink.ht_cap.ht_supported)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+
+ ht = (struct sta_rec_ht *)tlv;
+- ht->ht_cap = cpu_to_le16(sta->ht_cap.cap);
++ ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+ }
+
+ static void
+@@ -909,15 +909,15 @@ mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+ struct sta_rec_vht *vht;
+ struct tlv *tlv;
+
+- if (!sta->vht_cap.vht_supported)
++ if (!sta->deflink.vht_cap.vht_supported)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht));
+
+ vht = (struct sta_rec_vht *)tlv;
+- vht->vht_cap = cpu_to_le32(sta->vht_cap.cap);
+- vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map;
+- vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map;
++ vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
++ vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
++ vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
+ }
+
+ static void
+@@ -932,7 +932,7 @@ mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ vif->type != NL80211_IFTYPE_AP)
+ return;
+
+- if (!sta->max_amsdu_len)
++ if (!sta->deflink.agg.max_amsdu_len)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu));
+@@ -941,7 +941,7 @@ mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ amsdu->amsdu_en = true;
+ msta->wcid.amsdu = true;
+
+- switch (sta->max_amsdu_len) {
++ switch (sta->deflink.agg.max_amsdu_len) {
+ case IEEE80211_MAX_MPDU_LEN_VHT_11454:
+ if (!is_mt7915(&dev->mt76)) {
+ amsdu->max_mpdu_size =
+@@ -1004,8 +1004,8 @@ mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ if (!bfee && tx_ant < 2)
+ return false;
+
+- if (sta->he_cap.has_he) {
+- struct ieee80211_he_cap_elem *pe = &sta->he_cap.he_cap_elem;
++ if (sta->deflink.he_cap.has_he) {
++ struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+
+ if (bfee)
+ return mvif->cap.he_su_ebfee &&
+@@ -1015,8 +1015,8 @@ mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ }
+
+- if (sta->vht_cap.vht_supported) {
+- u32 cap = sta->vht_cap.cap;
++ if (sta->deflink.vht_cap.vht_supported) {
++ u32 cap = sta->deflink.vht_cap.cap;
+
+ if (bfee)
+ return mvif->cap.vht_su_ebfee &&
+@@ -1042,7 +1042,7 @@ static void
+ mt7915_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
+ struct sta_rec_bf *bf)
+ {
+- struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
++ struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
+ u8 n = 0;
+
+ bf->tx_mode = MT_PHY_TYPE_HT;
+@@ -1067,7 +1067,7 @@ static void
+ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
+ struct sta_rec_bf *bf, bool explicit)
+ {
+- struct ieee80211_sta_vht_cap *pc = &sta->vht_cap;
++ struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
+ struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
+ u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
+ u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
+@@ -1088,14 +1088,14 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_160)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ bf->nrow = 1;
+ } else {
+ bf->nrow = tx_ant;
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = nss_mcs;
+
+- if (sta->bandwidth == IEEE80211_STA_RX_BW_160)
++ if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
+ bf->ibf_nrow = 1;
+ }
+ }
+@@ -1104,7 +1104,7 @@ static void
+ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ struct mt7915_phy *phy, struct sta_rec_bf *bf)
+ {
+- struct ieee80211_sta_he_cap *pc = &sta->he_cap;
++ struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
+ struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
+ const struct ieee80211_sta_he_cap *vc =
+ mt76_connac_get_he_phy_cap(phy->mt76, vif);
+@@ -1129,7 +1129,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+- if (sta->bandwidth != IEEE80211_STA_RX_BW_160)
++ if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
+ return;
+
+ /* go over for 160MHz and 80p80 */
+@@ -1177,7 +1177,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ };
+ bool ebf;
+
+- if (!(sta->ht_cap.ht_supported || sta->he_cap.has_he))
++ if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ ebf = mt7915_is_ebf_supported(phy, vif, sta, false);
+@@ -1191,21 +1191,21 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ * vht: support eBF and iBF
+ * ht: iBF only, since mac80211 lacks of eBF support
+ */
+- if (sta->he_cap.has_he && ebf)
++ if (sta->deflink.he_cap.has_he && ebf)
+ mt7915_mcu_sta_bfer_he(sta, vif, phy, bf);
+- else if (sta->vht_cap.vht_supported)
++ else if (sta->deflink.vht_cap.vht_supported)
+ mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf);
+- else if (sta->ht_cap.ht_supported)
++ else if (sta->deflink.ht_cap.ht_supported)
+ mt7915_mcu_sta_bfer_ht(sta, phy, bf);
+ else
+ return;
+
+ bf->bf_cap = ebf ? ebf : dev->ibf << 1;
+- bf->bw = sta->bandwidth;
+- bf->ibf_dbw = sta->bandwidth;
++ bf->bw = sta->deflink.bandwidth;
++ bf->ibf_dbw = sta->deflink.bandwidth;
+ bf->ibf_nrow = tx_ant;
+
+- if (!ebf && sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
++ if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
+ bf->ibf_timeout = 0x48;
+ else
+ bf->ibf_timeout = 0x18;
+@@ -1215,7 +1215,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ else
+ bf->mem_20m = matrix[bf->nrow][bf->ncol];
+
+- switch (sta->bandwidth) {
++ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ case IEEE80211_STA_RX_BW_80:
+ bf->mem_total = bf->mem_20m * 2;
+@@ -1240,7 +1240,7 @@ mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ struct tlv *tlv;
+ u8 nrow = 0;
+
+- if (!(sta->vht_cap.vht_supported || sta->he_cap.has_he))
++ if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ if (!mt7915_is_ebf_supported(phy, vif, sta, true))
+@@ -1249,13 +1249,13 @@ mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee));
+ bfee = (struct sta_rec_bfee *)tlv;
+
+- if (sta->he_cap.has_he) {
+- struct ieee80211_he_cap_elem *pe = &sta->he_cap.he_cap_elem;
++ if (sta->deflink.he_cap.has_he) {
++ struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+
+ nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ pe->phy_cap_info[5]);
+- } else if (sta->vht_cap.vht_supported) {
+- struct ieee80211_sta_vht_cap *pc = &sta->vht_cap;
++ } else if (sta->deflink.vht_cap.vht_supported) {
++ struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
+
+ nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ pc->cap);
+@@ -1311,7 +1311,7 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
+ ra->phy = *phy;
+ break;
+ case RATE_PARAM_MMPS_UPDATE:
+- ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->smps_mode);
++ ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode);
+ break;
+ case RATE_PARAM_SPE_UPDATE:
+ ra->spe_idx = *(u8 *)data;
+@@ -1386,7 +1386,7 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
+ do { \
+ u8 i, gi = mask->control[band]._gi; \
+ gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \
+- for (i = 0; i <= sta->bandwidth; i++) { \
++ for (i = 0; i <= sta->deflink.bandwidth; i++) { \
+ phy.sgi |= gi << (i << (_he)); \
+ phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\
+ } \
+@@ -1400,11 +1400,11 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
+ } \
+ } while (0)
+
+- if (sta->he_cap.has_he) {
++ if (sta->deflink.he_cap.has_he) {
+ __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
+- } else if (sta->vht_cap.vht_supported) {
++ } else if (sta->deflink.vht_cap.vht_supported) {
+ __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
+- } else if (sta->ht_cap.ht_supported) {
++ } else if (sta->deflink.ht_cap.ht_supported) {
+ __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
+ } else {
+ nrates = hweight32(mask->control[band].legacy);
+@@ -1438,7 +1438,7 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
+ * actual txrate hardware sends out.
+ */
+ addr = mt7915_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
+- if (sta->he_cap.has_he)
++ if (sta->deflink.he_cap.has_he)
+ mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
+ else
+ mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
+@@ -1471,7 +1471,7 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ enum nl80211_band band = chandef->chan->band;
+ struct sta_rec_ra *ra;
+ struct tlv *tlv;
+- u32 supp_rate = sta->supp_rates[band];
++ u32 supp_rate = sta->deflink.supp_rates[band];
+ u32 cap = sta->wme ? STA_CAP_WMM : 0;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
+@@ -1481,9 +1481,9 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ ra->auto_rate = true;
+ ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
+ ra->channel = chandef->chan->hw_value;
+- ra->bw = sta->bandwidth;
+- ra->phy.bw = sta->bandwidth;
+- ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->smps_mode);
++ ra->bw = sta->deflink.bandwidth;
++ ra->phy.bw = sta->deflink.bandwidth;
++ ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode);
+
+ if (supp_rate) {
+ supp_rate &= mask->control[band].legacy;
+@@ -1503,22 +1503,22 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ }
+ }
+
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ ra->supp_mode |= MODE_HT;
+- ra->af = sta->ht_cap.ampdu_factor;
+- ra->ht_gf = !!(sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
++ ra->af = sta->deflink.ht_cap.ampdu_factor;
++ ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
+
+ cap |= STA_CAP_HT;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ cap |= STA_CAP_SGI_20;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ cap |= STA_CAP_SGI_40;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
+ cap |= STA_CAP_TX_STBC;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ cap |= STA_CAP_RX_STBC;
+ if (mvif->cap.ht_ldpc &&
+- (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
++ (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ cap |= STA_CAP_LDPC;
+
+ mt7915_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
+@@ -1526,37 +1526,37 @@ mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
+ ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ }
+
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ u8 af;
+
+ ra->supp_mode |= MODE_VHT;
+ af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+- sta->vht_cap.cap);
++ sta->deflink.vht_cap.cap);
+ ra->af = max_t(u8, ra->af, af);
+
+ cap |= STA_CAP_VHT;
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
+ cap |= STA_CAP_VHT_SGI_80;
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
+ cap |= STA_CAP_VHT_SGI_160;
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ cap |= STA_CAP_VHT_TX_STBC;
+- if (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
++ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ cap |= STA_CAP_VHT_RX_STBC;
+ if (mvif->cap.vht_ldpc &&
+- (sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
++ (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ cap |= STA_CAP_VHT_LDPC;
+
+ mt7915_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
+ mask->control[band].vht_mcs);
+ }
+
+- if (sta->he_cap.has_he) {
++ if (sta->deflink.he_cap.has_he) {
+ ra->supp_mode |= MODE_HE;
+ cap |= STA_CAP_HE;
+
+- if (sta->he_6ghz_capa.capa)
+- ra->af = le16_get_bits(sta->he_6ghz_capa.capa,
++ if (sta->deflink.he_6ghz_capa.capa)
++ ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ }
+
+@@ -1765,7 +1765,7 @@ mt7915_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ if (!offs->cntdwn_counter_offs[0])
+ return;
+
+- sub_tag = vif->csa_active ? BSS_INFO_BCN_CSA : BSS_INFO_BCN_BCC;
++ sub_tag = vif->bss_conf.csa_active ? BSS_INFO_BCN_CSA : BSS_INFO_BCN_BCC;
+ tlv = mt7915_mcu_add_nested_subtlv(rskb, sub_tag, sizeof(*info),
+ &bcn->sub_ntlv, &bcn->len);
+ info = (struct bss_info_bcn_cntdwn *)tlv;
+@@ -1850,9 +1850,9 @@ mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ if (offs->cntdwn_counter_offs[0]) {
+ u16 offset = offs->cntdwn_counter_offs[0];
+
+- if (vif->csa_active)
++ if (vif->bss_conf.csa_active)
+ cont->csa_ofs = cpu_to_le16(offset - 4);
+- if (vif->color_change_active)
++ if (vif->bss_conf.color_change_active)
+ cont->bcc_ofs = cpu_to_le16(offset - 3);
+ }
+
+@@ -2037,7 +2037,7 @@ int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ if (!en)
+ goto out;
+
+- skb = ieee80211_beacon_get_template(hw, vif, &offs);
++ skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+ if (!skb)
+ return -EINVAL;
+
+@@ -3162,17 +3162,17 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+ if (txpower) {
+ u32 offs, len, i;
+
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ const u8 *sku_len = mt7915_sku_group_len;
+
+ offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM];
+ len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40];
+
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ offs += len;
+ len = sku_len[SKU_VHT_BW20] * 4;
+
+- if (sta->he_cap.has_he) {
++ if (sta->deflink.he_cap.has_he) {
+ offs += len + sku_len[SKU_HE_RU26] * 3;
+ len = sku_len[SKU_HE_RU242] * 4;
+ }
+diff --git a/mt7915/soc.c b/mt7915/soc.c
+index 8b398d577369..c06c56a0270d 100644
+--- a/mt7915/soc.c
++++ b/mt7915/soc.c
+@@ -4,6 +4,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
++#include <linux/pinctrl/consumer.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+ #include <linux/of_reserved_mem.h>
+diff --git a/mt7921/dma.c b/mt7921/dma.c
+index 6456c9a6175a..d1f10f6d9adc 100644
+--- a/mt7921/dma.c
++++ b/mt7921/dma.c
+@@ -283,8 +283,8 @@ int mt7921_dma_init(struct mt7921_dev *dev)
+ if (ret < 0)
+ return ret;
+
+- netif_tx_napi_add(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+- mt7921_poll_tx, NAPI_POLL_WEIGHT);
++ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
++ mt7921_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ return mt7921_dma_enable(dev);
+diff --git a/mt7921/init.c b/mt7921/init.c
+index e42cb6be4055..0d8db2bd6a25 100644
+--- a/mt7921/init.c
++++ b/mt7921/init.c
+@@ -55,8 +55,8 @@ mt7921_init_wiphy(struct ieee80211_hw *hw)
+ struct wiphy *wiphy = hw->wiphy;
+
+ hw->queues = 4;
+- hw->max_rx_aggregation_subframes = 256;
+- hw->max_tx_aggregation_subframes = 256;
++ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
++ hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
+ hw->netdev_features = NETIF_F_RXCSUM;
+
+ hw->radiotap_timestamp.units_pos =
+diff --git a/mt7921/mac.c b/mt7921/mac.c
+index 639614b04c5f..2d2b53d6cd47 100644
+--- a/mt7921/mac.c
++++ b/mt7921/mac.c
+@@ -504,7 +504,7 @@ static void mt7921_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+ u16 fc, tid;
+ u32 val;
+
+- if (!sta || !(sta->ht_cap.ht_supported || sta->he_cap.has_he))
++ if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+diff --git a/mt7921/main.c b/mt7921/main.c
+index 1b7219e37d19..3e42dcaa4391 100644
+--- a/mt7921/main.c
++++ b/mt7921/main.c
+@@ -171,7 +171,8 @@ mt7921_init_he_caps(struct mt7921_phy *phy, enum nl80211_band band,
+ mt7921_gen_ppe_thresh(he_cap->ppe_thres, nss);
+ } else {
+ he_cap_elem->phy_cap_info[9] |=
+- IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US;
++ u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US,
++ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK);
+ }
+
+ if (band == NL80211_BAND_6GHZ) {
+@@ -690,7 +691,8 @@ out:
+ }
+
+ static int
+-mt7921_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
++mt7921_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+@@ -760,7 +762,7 @@ static void mt7921_configure_filter(struct ieee80211_hw *hw,
+ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+@@ -791,7 +793,7 @@ static void mt7921_bss_info_changed(struct ieee80211_hw *hw,
+ if (changed & BSS_CHANGED_ASSOC) {
+ mt7921_mcu_sta_update(dev, NULL, vif, true,
+ MT76_STA_INFO_STATE_ASSOC);
+- mt7921_mcu_set_beacon_filter(dev, vif, info->assoc);
++ mt7921_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
+ }
+
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+@@ -1638,8 +1640,8 @@ mt7921_channel_switch_beacon(struct ieee80211_hw *hw,
+ }
+
+ static int
+-mt7921_start_ap(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif)
++mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf)
+ {
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+@@ -1666,8 +1668,8 @@ out:
+ }
+
+ static void
+-mt7921_stop_ap(struct ieee80211_hw *hw,
+- struct ieee80211_vif *vif)
++mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_bss_conf *link_conf)
+ {
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+diff --git a/mt7921/mcu.c b/mt7921/mcu.c
+index b7ed744fa396..fb9c0f66cb27 100644
+--- a/mt7921/mcu.c
++++ b/mt7921/mcu.c
+@@ -862,7 +862,7 @@ int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif)
+ .ps = {
+ .tag = cpu_to_le16(UNI_BSS_INFO_PS),
+ .len = cpu_to_le16(sizeof(struct ps_tlv)),
+- .ps_state = vif->bss_conf.ps ? 2 : 0,
++ .ps_state = vif->cfg.ps ? 2 : 0,
+ },
+ };
+
+@@ -926,7 +926,7 @@ mt7921_mcu_set_bss_pm(struct mt7921_dev *dev, struct ieee80211_vif *vif,
+ u8 pad;
+ } req = {
+ .bss_idx = mvif->mt76.idx,
+- .aid = cpu_to_le16(vif->bss_conf.aid),
++ .aid = cpu_to_le16(vif->cfg.aid),
+ .dtim_period = vif->bss_conf.dtim_period,
+ .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
+ };
+@@ -1143,7 +1143,7 @@ mt7921_mcu_uni_add_beacon_offload(struct mt7921_dev *dev,
+ if (!enable)
+ return -EOPNOTSUPP;
+
+- skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs);
++ skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0);
+ if (!skb)
+ return -EINVAL;
+
+diff --git a/mt7921/pci_mac.c b/mt7921/pci_mac.c
+index 436f07ba9629..8dd60408b117 100644
+--- a/mt7921/pci_mac.c
++++ b/mt7921/pci_mac.c
+@@ -113,7 +113,7 @@ int mt7921e_mac_reset(struct mt7921_dev *dev)
+
+ err = mt7921e_driver_own(dev);
+ if (err)
+- return err;
++ goto out;
+
+ err = mt7921_run_firmware(dev);
+ if (err)
+diff --git a/tx.c b/tx.c
+index c8d78b0a5d84..24568b98ed9d 100644
+--- a/tx.c
++++ b/tx.c
+@@ -60,15 +60,20 @@ mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
+ .skb = skb,
+ .info = IEEE80211_SKB_CB(skb),
+ };
++ struct ieee80211_rate_status rs = {};
+ struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
+ struct mt76_wcid *wcid;
+
+ wcid = rcu_dereference(dev->wcid[cb->wcid]);
+ if (wcid) {
+ status.sta = wcid_to_sta(wcid);
+-
+- if (status.sta)
+- status.rate = &wcid->rate;
++ if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) {
++ rs.rate_idx = wcid->rate;
++ status.rates = &rs;
++ status.n_rates = 1;
++ } else {
++ status.n_rates = 0;
++ }
+ }
+
+ hw = mt76_tx_status_get_hw(dev, skb);
+diff --git a/usb.c b/usb.c
+index 369c27ab9259..3e281715fcd4 100644
+--- a/usb.c
++++ b/usb.c
+@@ -1075,7 +1075,7 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
+
+ INIT_WORK(&usb->stat_work, mt76u_tx_status_data);
+
+- usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1);
++ usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0));
+ if (usb->data_len < 32)
+ usb->data_len = 32;
+
+--
+2.38.1
+
-I$(STAGING_DIR)/usr/include/mac80211-backport \
-I$(STAGING_DIR)/usr/include/mac80211/uapi \
-I$(STAGING_DIR)/usr/include/mac80211 \
- -include backport/backport.h
+ -include backport/backport.h \
+ -Wno-unused-result
define Build/Compile
+$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \
--- /dev/null
+--- a/core.c
++++ b/core.c
+@@ -692,7 +692,7 @@ static void mwl_chnl_switch_event(struct
+ vif = container_of((void *)mwl_vif, struct ieee80211_vif,
+ drv_priv);
+
+- if (vif->csa_active)
++ if (vif->bss_conf.csa_active)
+ ieee80211_csa_finish(vif);
+ }
+ spin_unlock_bh(&priv->vif_lock);
+--- a/debugfs.c
++++ b/debugfs.c
+@@ -462,9 +462,9 @@ static ssize_t mwl_debugfs_vif_read(stru
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ len += scnprintf(p + len, size - len, "type: ap\n");
+- memcpy(ssid, vif->bss_conf.ssid,
+- vif->bss_conf.ssid_len);
+- ssid[vif->bss_conf.ssid_len] = 0;
++ memcpy(ssid, vif->cfg.ssid,
++ vif->cfg.ssid_len);
++ ssid[vif->cfg.ssid_len] = 0;
+ len += scnprintf(p + len, size - len,
+ "ssid: %s\n", ssid);
+ len += scnprintf(p + len, size - len,
+@@ -486,8 +486,8 @@ static ssize_t mwl_debugfs_vif_read(stru
+ "type: unknown\n");
+ break;
+ }
+- if (vif->chanctx_conf) {
+- chan_def = &vif->chanctx_conf->def;
++ if (vif->bss_conf.chanctx_conf) {
++ chan_def = &vif->bss_conf.chanctx_conf->def;
+ len += scnprintf(p + len, size - len,
+ "channel: %d: width: %d\n",
+ chan_def->chan->hw_value,
+@@ -573,28 +573,28 @@ static ssize_t mwl_debugfs_sta_read(stru
+ "amsdu cap: 0x%02x\n",
+ sta_info->amsdu_ctrl.cap);
+ }
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ len += scnprintf(p + len, size - len,
+ "ht_cap: 0x%04x, ampdu: %02x, %02x\n",
+- sta->ht_cap.cap,
+- sta->ht_cap.ampdu_factor,
+- sta->ht_cap.ampdu_density);
++ sta->deflink.ht_cap.cap,
++ sta->deflink.ht_cap.ampdu_factor,
++ sta->deflink.ht_cap.ampdu_density);
+ len += scnprintf(p + len, size - len,
+ "rx_mask: 0x%02x, %02x, %02x, %02x\n",
+- sta->ht_cap.mcs.rx_mask[0],
+- sta->ht_cap.mcs.rx_mask[1],
+- sta->ht_cap.mcs.rx_mask[2],
+- sta->ht_cap.mcs.rx_mask[3]);
++ sta->deflink.ht_cap.mcs.rx_mask[0],
++ sta->deflink.ht_cap.mcs.rx_mask[1],
++ sta->deflink.ht_cap.mcs.rx_mask[2],
++ sta->deflink.ht_cap.mcs.rx_mask[3]);
+ }
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ len += scnprintf(p + len, size - len,
+ "vht_cap: 0x%08x, mcs: %02x, %02x\n",
+- sta->vht_cap.cap,
+- sta->vht_cap.vht_mcs.rx_mcs_map,
+- sta->vht_cap.vht_mcs.tx_mcs_map);
++ sta->deflink.vht_cap.cap,
++ sta->deflink.vht_cap.vht_mcs.rx_mcs_map,
++ sta->deflink.vht_cap.vht_mcs.tx_mcs_map);
+ }
+ len += scnprintf(p + len, size - len, "rx_bw: %d, rx_nss: %d\n",
+- sta->bandwidth, sta->rx_nss);
++ sta->deflink.bandwidth, sta->deflink.rx_nss);
+ len += scnprintf(p + len, size - len,
+ "tdls: %d, tdls_init: %d\n",
+ sta->tdls, sta->tdls_initiator);
+--- a/hif/fwcmd.c
++++ b/hif/fwcmd.c
+@@ -634,8 +634,9 @@ einval:
+
+ static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv,
+ struct mwl_vif *mwl_vif,
+- struct ieee80211_bss_conf *bss_conf)
++ struct ieee80211_vif *vif)
+ {
++ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ struct hostcmd_cmd_ap_beacon *pcmd;
+ struct ds_params *phy_ds_param_set;
+
+@@ -664,7 +665,7 @@ static int mwl_fwcmd_set_ap_beacon(struc
+ pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+ ether_addr_copy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid);
+- memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len);
++ memcpy(pcmd->start_cmd.ssid, vif->cfg.ssid, vif->cfg.ssid_len);
+ if (priv->chip_type == MWL8997)
+ ether_addr_copy(pcmd->start_cmd.bssid, mwl_vif->bssid);
+ pcmd->start_cmd.bss_type = 1;
+@@ -2085,7 +2086,7 @@ int mwl_fwcmd_set_beacon(struct ieee8021
+ if (mwl_fwcmd_set_wsc_ie(hw, b_inf->ie_wsc_len, b_inf->ie_wsc_ptr))
+ goto err;
+
+- if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf))
++ if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, vif))
+ goto err;
+
+ if (b_inf->cap_info & WLAN_CAPABILITY_SPECTRUM_MGMT)
+@@ -2147,38 +2148,38 @@ int mwl_fwcmd_set_new_stn_add(struct iee
+ ether_addr_copy(pcmd->mac_addr, sta->addr);
+
+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
+- rates = sta->supp_rates[NL80211_BAND_2GHZ];
++ rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ];
+ else
+- rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
++ rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5;
+ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
+
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+- if (i < sta->rx_nss) {
++ if (i < sta->deflink.rx_nss) {
+ pcmd->peer_info.ht_rates[i] =
+- sta->ht_cap.mcs.rx_mask[i];
++ sta->deflink.ht_cap.mcs.rx_mask[i];
+ } else {
+ pcmd->peer_info.ht_rates[i] = 0;
+ }
+ }
+- pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
++ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->deflink.ht_cap.cap);
+ pcmd->peer_info.mac_ht_param_info =
+- (sta->ht_cap.ampdu_factor & 3) |
+- ((sta->ht_cap.ampdu_density & 7) << 2);
++ (sta->deflink.ht_cap.ampdu_factor & 3) |
++ ((sta->deflink.ht_cap.ampdu_density & 7) << 2);
+ }
+
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ u32 rx_mcs_map_mask = 0;
+
+- rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2))
+- << (sta->rx_nss * 2);
++ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->deflink.rx_nss * 2))
++ << (sta->deflink.rx_nss * 2);
+ pcmd->peer_info.vht_max_rx_mcs =
+ cpu_to_le32((*((u32 *)
+- &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask);
+- pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
+- pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
++ &sta->deflink.vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask);
++ pcmd->peer_info.vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
++ pcmd->peer_info.vht_rx_channel_width = sta->deflink.bandwidth;
+ }
+
+ pcmd->is_qos_sta = sta->wme;
+@@ -2234,38 +2235,38 @@ int mwl_fwcmd_set_new_stn_add_sc4(struct
+ ether_addr_copy(pcmd->mac_addr, sta->addr);
+
+ if (hw->conf.chandef.chan->band == NL80211_BAND_2GHZ)
+- rates = sta->supp_rates[NL80211_BAND_2GHZ];
++ rates = sta->deflink.supp_rates[NL80211_BAND_2GHZ];
+ else
+- rates = sta->supp_rates[NL80211_BAND_5GHZ] << 5;
++ rates = sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 5;
+ pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
+
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+- if (i < sta->rx_nss) {
++ if (i < sta->deflink.rx_nss) {
+ pcmd->peer_info.ht_rates[i] =
+- sta->ht_cap.mcs.rx_mask[i];
++ sta->deflink.ht_cap.mcs.rx_mask[i];
+ } else {
+ pcmd->peer_info.ht_rates[i] = 0;
+ }
+ }
+- pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
++ pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->deflink.ht_cap.cap);
+ pcmd->peer_info.mac_ht_param_info =
+- (sta->ht_cap.ampdu_factor & 3) |
+- ((sta->ht_cap.ampdu_density & 7) << 2);
++ (sta->deflink.ht_cap.ampdu_factor & 3) |
++ ((sta->deflink.ht_cap.ampdu_density & 7) << 2);
+ }
+
+- if (sta->vht_cap.vht_supported) {
++ if (sta->deflink.vht_cap.vht_supported) {
+ u32 rx_mcs_map_mask = 0;
+
+- rx_mcs_map_mask = ((0x0000FFFF) >> (sta->rx_nss * 2))
+- << (sta->rx_nss * 2);
++ rx_mcs_map_mask = ((0x0000FFFF) >> (sta->deflink.rx_nss * 2))
++ << (sta->deflink.rx_nss * 2);
+ pcmd->peer_info.vht_max_rx_mcs =
+ cpu_to_le32((*((u32 *)
+- &sta->vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask);
+- pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
+- pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
++ &sta->deflink.vht_cap.vht_mcs.rx_mcs_map)) | rx_mcs_map_mask);
++ pcmd->peer_info.vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
++ pcmd->peer_info.vht_rx_channel_width = sta->deflink.bandwidth;
+ }
+
+ pcmd->is_qos_sta = sta->wme;
+@@ -2782,9 +2783,9 @@ int mwl_fwcmd_create_ba(struct ieee80211
+ pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags);
+ pcmd->ba_info.create_params.queue_id = stream->idx;
+ pcmd->ba_info.create_params.param_info =
+- (stream->sta->ht_cap.ampdu_factor &
++ (stream->sta->deflink.ht_cap.ampdu_factor &
+ IEEE80211_HT_AMPDU_PARM_FACTOR) |
+- ((stream->sta->ht_cap.ampdu_density << 2) &
++ ((stream->sta->deflink.ht_cap.ampdu_density << 2) &
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
+ if (direction == BA_FLAG_DIRECTION_UP) {
+ pcmd->ba_info.create_params.reset_seq_no = 0;
+@@ -2794,9 +2795,9 @@ int mwl_fwcmd_create_ba(struct ieee80211
+ pcmd->ba_info.create_params.current_seq = cpu_to_le16(0);
+ }
+ if (priv->chip_type == MWL8964 &&
+- stream->sta->vht_cap.vht_supported) {
++ stream->sta->deflink.vht_cap.vht_supported) {
+ pcmd->ba_info.create_params.vht_rx_factor =
+- cpu_to_le32((stream->sta->vht_cap.cap &
++ cpu_to_le32((stream->sta->deflink.vht_cap.cap &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
+ }
+--- a/mac80211.c
++++ b/mac80211.c
+@@ -371,15 +371,15 @@ static void mwl_mac80211_bss_info_change
+ }
+ }
+
+- if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc)
++ if ((changed & BSS_CHANGED_ASSOC) && vif->cfg.assoc)
+ mwl_fwcmd_set_aid(hw, vif, (u8 *)vif->bss_conf.bssid,
+- vif->bss_conf.aid);
++ vif->cfg.aid);
+ }
+
+ static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ struct mwl_priv *priv = hw->priv;
+ struct mwl_vif *mwl_vif;
+@@ -429,8 +429,8 @@ static void mwl_mac80211_bss_info_change
+ if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
+ struct sk_buff *skb;
+
+- if ((info->ssid[0] != '\0') &&
+- (info->ssid_len != 0) &&
++ if ((vif->cfg.ssid[0] != '\0') &&
++ (vif->cfg.ssid_len != 0) &&
+ (!info->hidden_ssid)) {
+ if (mwl_vif->broadcast_ssid != true) {
+ mwl_fwcmd_broadcast_ssid_enable(hw, vif, true);
+@@ -444,7 +444,7 @@ static void mwl_mac80211_bss_info_change
+ }
+
+ if (!mwl_vif->set_beacon) {
+- skb = ieee80211_beacon_get(hw, vif);
++ skb = ieee80211_beacon_get(hw, vif, 0);
+
+ if (skb) {
+ mwl_fwcmd_set_beacon(hw, vif, skb->data, skb->len);
+@@ -461,7 +461,7 @@ static void mwl_mac80211_bss_info_change
+ static void mwl_mac80211_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+- u32 changed)
++ u64 changed)
+ {
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+@@ -583,10 +583,10 @@ static int mwl_mac80211_sta_add(struct i
+ if (vif->type == NL80211_IFTYPE_MESH_POINT)
+ sta_info->is_mesh_node = true;
+
+- if (sta->ht_cap.ht_supported) {
++ if (sta->deflink.ht_cap.ht_supported) {
+ sta_info->is_ampdu_allowed = true;
+ sta_info->is_amsdu_allowed = false;
+- if (sta->ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
++ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_8K;
+ else
+ sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_4K;
+@@ -668,7 +668,7 @@ static int mwl_mac80211_sta_remove(struc
+
+ static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+- u16 queue,
++ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct mwl_priv *priv = hw->priv;
+@@ -768,6 +768,7 @@ static int mwl_mac80211_ampdu_action(str
+ spin_lock_bh(&priv->stream_lock);
+ break;
+ }
++ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ if (priv->chip_type == MWL8964) {
+ struct mwl_ampdu_stream tmp;
+@@ -930,4 +931,5 @@ const struct ieee80211_ops mwl_mac80211_
+ .pre_channel_switch = mwl_mac80211_chnl_switch,
+ .sw_scan_start = mwl_mac80211_sw_scan_start,
+ .sw_scan_complete = mwl_mac80211_sw_scan_complete,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ };
+--- a/utils.c
++++ b/utils.c
+@@ -173,9 +173,9 @@ u32 utils_get_init_tx_rate(struct mwl_pr
+ u32 tx_rate;
+ u16 format, nss, bw, rate_mcs;
+
+- if (sta->vht_cap.vht_supported)
++ if (sta->deflink.vht_cap.vht_supported)
+ format = TX_RATE_FORMAT_11AC;
+- else if (sta->ht_cap.ht_supported)
++ else if (sta->deflink.ht_cap.ht_supported)
+ format = TX_RATE_FORMAT_11N;
+ else
+ format = TX_RATE_FORMAT_LEGACY;
+@@ -192,11 +192,11 @@ u32 utils_get_init_tx_rate(struct mwl_pr
+ nss = 3;
+ break;
+ default:
+- nss = sta->rx_nss;
++ nss = sta->deflink.rx_nss;
+ break;
+ }
+- if (nss > sta->rx_nss)
+- nss = sta->rx_nss;
++ if (nss > sta->deflink.rx_nss)
++ nss = sta->deflink.rx_nss;
+
+ switch (conf->chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+@@ -213,11 +213,11 @@ u32 utils_get_init_tx_rate(struct mwl_pr
+ bw = TX_RATE_BANDWIDTH_160;
+ break;
+ default:
+- bw = sta->bandwidth;
++ bw = sta->deflink.bandwidth;
+ break;
+ }
+- if (bw > sta->bandwidth)
+- bw = sta->bandwidth;
++ if (bw > sta->deflink.bandwidth)
++ bw = sta->deflink.bandwidth;
+
+ switch (format) {
+ case TX_RATE_FORMAT_LEGACY:
+--- a/hif/pcie/tx.c
++++ b/hif/pcie/tx.c
+@@ -157,7 +157,7 @@ static int pcie_txbd_ring_create(struct
+ wiphy_info(priv->hw->wiphy,
+ "TX ring: - base: %p, pbase: 0x%x, len: %d\n",
+ pcie_priv->txbd_ring_vbase,
+- pcie_priv->txbd_ring_pbase,
++ (u32)pcie_priv->txbd_ring_pbase,
+ pcie_priv->txbd_ring_size);
+
+ for (num = 0; num < PCIE_MAX_TXRX_BD; num++) {
+@@ -1153,7 +1153,7 @@ void pcie_tx_xmit(struct ieee80211_hw *h
+ index = SYSADPT_TX_WMM_QUEUES - index - 1;
+ txpriority = index;
+
+- if (sta && sta->ht_cap.ht_supported && !eapol_frame &&
++ if (sta && sta->deflink.ht_cap.ht_supported && !eapol_frame &&
+ ieee80211_is_data_qos(wh->frame_control)) {
+ tid = qos & 0xf;
+ pcie_tx_count_packet(sta, tid);
+--- a/hif/pcie/tx_ndp.c
++++ b/hif/pcie/tx_ndp.c
+@@ -602,7 +602,7 @@ void pcie_tx_xmit_ndp(struct ieee80211_h
+ pcie_tx_encapsulate_frame(priv, skb, k_conf, NULL);
+ } else {
+ tid = qos & 0x7;
+- if (sta && sta->ht_cap.ht_supported && !eapol_frame &&
++ if (sta && sta->deflink.ht_cap.ht_supported && !eapol_frame &&
+ qos != 0xFFFF) {
+ pcie_tx_count_packet(sta, tid);
+ spin_lock_bh(&priv->stream_lock);
--- a/nl80211.h
+++ b/nl80211.h
-@@ -11,7 +11,7 @@
- * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
- * Copyright 2008 Colin McCabe <colin@cozybit.com>
- * Copyright 2015-2017 Intel Deutschland GmbH
-- * Copyright (C) 2018-2022 Intel Corporation
-+ * Copyright (C) 2018-2021 Intel Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
-@@ -301,29 +301,6 @@
+@@ -324,6 +324,17 @@
*/
/**
-- * DOC: FILS shared key crypto offload
-- *
-- * This feature is applicable to drivers running in AP mode.
-- *
-- * FILS shared key crypto offload can be advertised by drivers by setting
-- * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD flag. The drivers that support
-- * FILS shared key crypto offload should be able to encrypt and decrypt
-- * association frames for FILS shared key authentication as per IEEE 802.11ai.
-- * With this capability, for FILS key derivation, drivers depend on userspace.
-- *
-- * After FILS key derivation, userspace shares the FILS AAD details with the
-- * driver and the driver stores the same to use in decryption of association
-- * request and in encryption of association response. The below parameters
-- * should be given to the driver in %NL80211_CMD_SET_FILS_AAD.
-- * %NL80211_ATTR_MAC - STA MAC address, used for storing FILS AAD per STA
-- * %NL80211_ATTR_FILS_KEK - Used for encryption or decryption
-- * %NL80211_ATTR_FILS_NONCES - Used for encryption or decryption
-- * (STA Nonce 16 bytes followed by AP Nonce 16 bytes)
-- *
-- * Once the association is done, the driver cleans the FILS AAD data.
-- */
--
--/**
++ * DOC: Multi-Link Operation
++ *
++ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
++ * links. To use this in nl80211, various commands and responses now need
++ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
++ * Additionally, various commands that need to operate on a specific link
++ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
++ * use %NL80211_CMD_START_AP or similar functions.
++ */
++
++/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
-@@ -1226,17 +1203,6 @@
- * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
- * has completed
+@@ -366,14 +377,22 @@
+ * the non-transmitting interfaces are deleted as well.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+- * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
++ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
++ * represents peer's MLD address for MLO pairwise key. For MLO group key,
++ * the link is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
++ * For MLO connection, the link to set default key is identified by
++ * %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+- * and %NL80211_ATTR_KEY_SEQ attributes.
++ * and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
++ * peer's MLD address for MLO pairwise key. The link to add MLO
++ * group key is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+- * or %NL80211_ATTR_MAC.
++ * or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
++ * for MLO pairwise key. The link to delete group key is identified by
++ * %NL80211_ATTR_MLO_LINK_ID.
*
-- * @NL80211_CMD_SET_FILS_AAD: Set FILS AAD data to the driver using -
-- * &NL80211_ATTR_MAC - for STA MAC address
-- * &NL80211_ATTR_FILS_KEK - for KEK
-- * &NL80211_ATTR_FILS_NONCES - for FILS Nonces
-- * (STA Nonce 16 bytes followed by AP Nonce 16 bytes)
-- *
-- * @NL80211_CMD_ASSOC_COMEBACK: notification about an association
-- * temporal rejection with comeback. The event includes %NL80211_ATTR_MAC
-- * to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
-- * specify the timeout value.
-- *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+@@ -753,6 +772,13 @@
+ * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ * counters which will be updated to the current value. This attribute
+ * is used during CSA period.
++ * For TX on an MLD, the frequency can be omitted and the link ID be
++ * specified, or if transmitting to a known peer MLD (with MLD addresses
++ * in the frame) both can be omitted and the link will be selected by
++ * lower layers.
++ * For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
++ * indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
++ * be included to indicate the ack TX timestamp.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ * command may be used with the corresponding cookie to cancel the wait
+ * time if it is known that it is no longer necessary. This command is
+@@ -763,7 +789,9 @@
+ * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
+ * the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+- * the frame.
++ * the frame. %NL80211_ATTR_TX_HW_TIMESTAMP may be included to indicate the
++ * tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
++ * indicate the ack RX timestamp.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
+ *
+@@ -1108,6 +1136,12 @@
+ * has been received. %NL80211_ATTR_FRAME is used to specify the
+ * frame contents. The frame is the raw EAPoL data, without ethernet or
+ * 802.11 headers.
++ * For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
++ * its effect will depend on the destination: If the destination is known
++ * to be an MLD, this will be used as a hint to select the link to transmit
++ * the frame on. If the destination is not an MLD, this will select both
++ * the link to transmit on and the source address will be set to the link
++ * address of that link.
+ * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ * indicating the protocol type of the received frame; whether the frame
+@@ -1237,6 +1271,16 @@
+ * to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
+ * specify the timeout value.
+ *
++ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
++ * %NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
++ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
++ * without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
++ * in preparation for e.g. roaming to a regular (non-MLO) AP.
++ *
++ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
++ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
++ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
++ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
-@@ -1477,10 +1443,6 @@ enum nl80211_commands {
- NL80211_CMD_COLOR_CHANGE_ABORTED,
- NL80211_CMD_COLOR_CHANGE_COMPLETED,
+@@ -1481,6 +1525,13 @@ enum nl80211_commands {
+
+ NL80211_CMD_ASSOC_COMEBACK,
-- NL80211_CMD_SET_FILS_AAD,
--
-- NL80211_CMD_ASSOC_COMEBACK,
--
++ NL80211_CMD_ADD_LINK,
++ NL80211_CMD_REMOVE_LINK,
++
++ NL80211_CMD_ADD_LINK_STA,
++ NL80211_CMD_MODIFY_LINK_STA,
++ NL80211_CMD_REMOVE_LINK_STA,
++
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
-@@ -2477,9 +2439,7 @@ enum nl80211_commands {
- * space supports external authentication. This attribute shall be used
- * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
- * may offload authentication processing to user space if this capability
-- * is indicated in the respective requests from the user space. (This flag
-- * attribute deprecated for %NL80211_CMD_START_AP, use
-- * %NL80211_ATTR_AP_SETTINGS_FLAGS)
-+ * is indicated in the respective requests from the user space.
+@@ -2340,8 +2391,10 @@ enum nl80211_commands {
*
- * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
- * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
-@@ -2655,13 +2615,8 @@ enum nl80211_commands {
- * switching on a different channel during CAC detection on the selected
- * radar channel.
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+- * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+- * interface type.
++ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
++ * other interface-type specific capabilities per interface type. For MLO,
++ * %NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
++ * present.
*
-- * @NL80211_ATTR_AP_SETTINGS_FLAGS: u32 attribute contains ap settings flags,
-- * enumerated in &enum nl80211_ap_settings_flags. This attribute shall be
-- * used with %NL80211_CMD_START_AP request.
-- *
-- * @NL80211_ATTR_EHT_CAPABILITY: EHT Capability information element (from
-- * association request when used with NL80211_CMD_NEW_STATION). Can be set
-- * only if %NL80211_STA_FLAG_WME is set.
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ * groupID for monitor mode.
+@@ -2663,6 +2716,44 @@ enum nl80211_commands {
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if %NL80211_STA_FLAG_WME is set.
+ *
++ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
++ * various commands that need a link ID to operate.
++ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
++ * per-link information and a link ID.
++ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
++ * authenticate/associate.
++ *
++ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
++ * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
++ * included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
++ *
++ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
++ * AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
++ * %NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
++ * attribute is not present userspace shall consider maximum number of AKM
++ * suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
++ * number prior to the introduction of this attribute.
++ *
++ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
++ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
++ *
++ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
++ * nanoseconds (u64). This is the device clock timestamp so it will
++ * probably reset when the device is stopped or the firmware is reset.
++ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
++ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
++ * the ack TX timestamp.
++ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
++ * nanoseconds (u64). This is the device clock timestamp so it will
++ * probably reset when the device is stopped or the firmware is reset.
++ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
++ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
++ * the incoming frame RX timestamp.
++ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
++ * (re)associations.
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+ * transmit power to stay within regulatory limits. u32, dBi.
- *
++ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
-@@ -3171,11 +3126,7 @@ enum nl80211_attrs {
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3177,6 +3268,23 @@ enum nl80211_attrs {
- NL80211_ATTR_RADAR_BACKGROUND,
+ NL80211_ATTR_DISABLE_EHT,
-- NL80211_ATTR_AP_SETTINGS_FLAGS,
--
-- NL80211_ATTR_EHT_CAPABILITY,
--
-- NL80211_ATTR_DISABLE_EHT,
++ NL80211_ATTR_MLO_LINKS,
++ NL80211_ATTR_MLO_LINK_ID,
++ NL80211_ATTR_MLD_ADDR,
++
++ NL80211_ATTR_MLO_SUPPORT,
++
++ NL80211_ATTR_MAX_NUM_AKM_SUITES,
++
++ NL80211_ATTR_EML_CAPABILITY,
++ NL80211_ATTR_MLD_CAPA_AND_OPS,
++
++ NL80211_ATTR_TX_HW_TIMESTAMP,
++ NL80211_ATTR_RX_HW_TIMESTAMP,
++ NL80211_ATTR_TD_BITMAP,
++
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
-
++
/* add attributes here, update the policy in nl80211.c */
-@@ -3232,8 +3183,6 @@ enum nl80211_attrs {
+ __NL80211_ATTR_AFTER_LAST,
+@@ -3231,6 +3339,11 @@ enum nl80211_attrs {
+ #define NL80211_HE_MIN_CAPABILITY_LEN 16
#define NL80211_HE_MAX_CAPABILITY_LEN 54
#define NL80211_MAX_NR_CIPHER_SUITES 5
++
++/*
++ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
++ * present in %NL80211_CMD_GET_WIPHY response.
++ */
#define NL80211_MAX_NR_AKM_SUITES 2
--#define NL80211_EHT_MIN_CAPABILITY_LEN 13
--#define NL80211_EHT_MAX_CAPABILITY_LEN 51
-
- #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10
-
-@@ -3261,7 +3210,7 @@ enum nl80211_attrs {
- * and therefore can't be created in the normal ways, use the
- * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
- * commands to create and destroy one
-- * @NL80211_IFTYPE_OCB: Outside Context of a BSS
-+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
- * This mode corresponds to the MIB variable dot11OCBActivated=true
- * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
- * @NL80211_IFTYPE_MAX: highest interface type number currently defined
-@@ -3403,56 +3352,6 @@ enum nl80211_he_ru_alloc {
- };
-
- /**
-- * enum nl80211_eht_gi - EHT guard interval
-- * @NL80211_RATE_INFO_EHT_GI_0_8: 0.8 usec
-- * @NL80211_RATE_INFO_EHT_GI_1_6: 1.6 usec
-- * @NL80211_RATE_INFO_EHT_GI_3_2: 3.2 usec
-- */
--enum nl80211_eht_gi {
-- NL80211_RATE_INFO_EHT_GI_0_8,
-- NL80211_RATE_INFO_EHT_GI_1_6,
-- NL80211_RATE_INFO_EHT_GI_3_2,
--};
--
--/**
-- * enum nl80211_eht_ru_alloc - EHT RU allocation values
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_52: 52-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_52P26: 52+26-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_106: 106-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_106P26: 106+26 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_242: 242-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_484: 484-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_484P242: 484+242 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_996: 996-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484: 996+484 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242: 996+484+242 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996: 2x996-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484: 2x996+484 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996: 3x996-tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484: 3x996+484 tone RU allocation
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC_4x996: 4x996-tone RU allocation
-- */
--enum nl80211_eht_ru_alloc {
-- NL80211_RATE_INFO_EHT_RU_ALLOC_26,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_52,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_52P26,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_106,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_106P26,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_242,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_484,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_484P242,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_996,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_996P484,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_2x996,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_3x996,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484,
-- NL80211_RATE_INFO_EHT_RU_ALLOC_4x996,
--};
--
--/**
- * enum nl80211_rate_info - bitrate information
- *
- * These attribute types are used with %NL80211_STA_INFO_TXRATE
-@@ -3491,13 +3390,6 @@ enum nl80211_eht_ru_alloc {
- * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
- * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
- * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
-- * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
-- * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
-- * @NL80211_RATE_INFO_EHT_NSS: EHT NSS value (u8, 1-8)
-- * @NL80211_RATE_INFO_EHT_GI: EHT guard interval identifier
-- * (u8, see &enum nl80211_eht_gi)
-- * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
-- * non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
- * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ #define NL80211_EHT_MIN_CAPABILITY_LEN 13
+ #define NL80211_EHT_MAX_CAPABILITY_LEN 51
+@@ -4853,6 +4966,8 @@ enum nl80211_bss_scan_width {
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
++ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
++ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
*/
- enum nl80211_rate_info {
-@@ -3519,11 +3411,6 @@ enum nl80211_rate_info {
- NL80211_RATE_INFO_HE_GI,
- NL80211_RATE_INFO_HE_DCM,
- NL80211_RATE_INFO_HE_RU_ALLOC,
-- NL80211_RATE_INFO_320_MHZ_WIDTH,
-- NL80211_RATE_INFO_EHT_MCS,
-- NL80211_RATE_INFO_EHT_NSS,
-- NL80211_RATE_INFO_EHT_GI,
-- NL80211_RATE_INFO_EHT_RU_ALLOC,
+@@ -4878,6 +4993,8 @@ enum nl80211_bss {
+ NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
+ NL80211_BSS_FREQUENCY_OFFSET,
++ NL80211_BSS_MLO_LINK_ID,
++ NL80211_BSS_MLD_ADDR,
/* keep last */
- __NL80211_RATE_INFO_AFTER_LAST,
-@@ -3834,20 +3721,13 @@ enum nl80211_mpath_info {
- * capabilities IE
- * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
- * defined in HE capabilities IE
-+ * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently
-+ * defined
- * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
- * given for all 6 GHz band channels
- * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
- * advertised on this band/for this iftype (binary)
-- * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT
-- * capabilities element
-- * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: EHT PHY capabilities as in EHT
-- * capabilities element
-- * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: EHT supported NSS/MCS as in EHT
-- * capabilities element
-- * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
-- * defined in EHT capabilities element
- * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
-- * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
- */
- enum nl80211_band_iftype_attr {
- __NL80211_BAND_IFTYPE_ATTR_INVALID,
-@@ -3859,10 +3739,6 @@ enum nl80211_band_iftype_attr {
- NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
- NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
- NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
-- NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
-- NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
-- NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
-- NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
-
- /* keep last */
- __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
-@@ -4007,10 +3883,6 @@ enum nl80211_wmm_rule {
- * on this channel in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed
- * on this channel in current regulatory domain.
-- * @NL80211_FREQUENCY_ATTR_NO_320MHZ: any 320 MHz channel using this channel
-- * as the primary or any of the secondary channels isn't possible
-- * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
-- * in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
- * currently defined
- * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
-@@ -4047,8 +3919,6 @@ enum nl80211_frequency_attr {
- NL80211_FREQUENCY_ATTR_4MHZ,
- NL80211_FREQUENCY_ATTR_8MHZ,
- NL80211_FREQUENCY_ATTR_16MHZ,
-- NL80211_FREQUENCY_ATTR_NO_320MHZ,
-- NL80211_FREQUENCY_ATTR_NO_EHT,
-
- /* keep last */
- __NL80211_FREQUENCY_ATTR_AFTER_LAST,
-@@ -4247,7 +4117,6 @@ enum nl80211_sched_scan_match_attr {
- * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
- * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
- * @NL80211_RRF_NO_HE: HE operation not allowed
-- * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
- */
- enum nl80211_reg_rule_flags {
- NL80211_RRF_NO_OFDM = 1<<0,
-@@ -4266,7 +4135,6 @@ enum nl80211_reg_rule_flags {
- NL80211_RRF_NO_80MHZ = 1<<15,
- NL80211_RRF_NO_160MHZ = 1<<16,
- NL80211_RRF_NO_HE = 1<<17,
-- NL80211_RRF_NO_320MHZ = 1<<18,
- };
-
- #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
-@@ -4764,8 +4632,6 @@ enum nl80211_key_mode {
- * @NL80211_CHAN_WIDTH_4: 4 MHz OFDM channel
- * @NL80211_CHAN_WIDTH_8: 8 MHz OFDM channel
- * @NL80211_CHAN_WIDTH_16: 16 MHz OFDM channel
-- * @NL80211_CHAN_WIDTH_320: 320 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
-- * attribute must be provided as well
- */
- enum nl80211_chan_width {
- NL80211_CHAN_WIDTH_20_NOHT,
-@@ -4781,7 +4647,6 @@ enum nl80211_chan_width {
- NL80211_CHAN_WIDTH_4,
- NL80211_CHAN_WIDTH_8,
- NL80211_CHAN_WIDTH_16,
-- NL80211_CHAN_WIDTH_320,
- };
-
- /**
-@@ -5096,7 +4961,6 @@ enum nl80211_txrate_gi {
- * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
- * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
- * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
-- * @NL80211_BAND_LC: light communication band (placeholder)
- * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
- * since newer kernel versions may support more bands
- */
-@@ -5106,7 +4970,6 @@ enum nl80211_band {
- NL80211_BAND_60GHZ,
- NL80211_BAND_6GHZ,
- NL80211_BAND_S1GHZ,
-- NL80211_BAND_LC,
-
- NUM_NL80211_BANDS,
- };
-@@ -5673,7 +5536,7 @@ enum nl80211_iface_limit_attrs {
- * => allows 8 of AP/GO that can have BI gcd >= min gcd
- *
- * numbers = [ #{STA} <= 2 ], channels = 2, max = 2
-- * => allows two STAs on the same or on different channels
-+ * => allows two STAs on different channels
- *
- * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
- * => allows a STA plus three P2P interfaces
-@@ -5718,7 +5581,7 @@ enum nl80211_if_combination_attrs {
- * @NL80211_PLINK_ESTAB: mesh peer link is established
- * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
- * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
-- * plink are discarded, except for authentication frames
-+ * plink are discarded
- * @NUM_NL80211_PLINK_STATES: number of peer link states
- * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
- */
-@@ -5855,15 +5718,13 @@ enum nl80211_tdls_operation {
- NL80211_TDLS_DISABLE_LINK,
- };
-
--/**
-+/*
- * enum nl80211_ap_sme_features - device-integrated AP features
-- * @NL80211_AP_SME_SA_QUERY_OFFLOAD: SA Query procedures offloaded to driver
-- * when user space indicates support for SA Query procedures offload during
-- * "start ap" with %NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT.
-- */
-+ * Reserved for future use, no bits are defined in
-+ * NL80211_ATTR_DEVICE_AP_SME yet.
- enum nl80211_ap_sme_features {
-- NL80211_AP_SME_SA_QUERY_OFFLOAD = 1 << 0,
- };
-+ */
-
- /**
- * enum nl80211_feature_flags - device/driver features
-@@ -6166,11 +6027,6 @@ enum nl80211_feature_flags {
- * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
- * detection and change announcemnts.
- *
-- * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
-- * FILS encryption and decryption for (Re)Association Request and Response
-- * frames. Userspace has to share FILS AAD details to the driver by using
-- * @NL80211_CMD_SET_FILS_AAD.
-- *
+ __NL80211_BSS_AFTER_LAST,
+@@ -5874,7 +5991,7 @@ enum nl80211_ap_sme_features {
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ * the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+- * to work properly to suppport receiving regulatory hints from
++ * to work properly to support receiving regulatory hints from
+ * cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ * here to reserve the value for API/ABI compatibility)
+@@ -6174,6 +6291,14 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
* detection.
*
-@@ -6239,7 +6095,6 @@ enum nl80211_ext_feature_index {
- NL80211_EXT_FEATURE_SECURE_RTT,
- NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
++ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
++ * change without having to bring the underlying network device down
++ * first. For example, in station mode this can be used to vary the
++ * origin MAC address prior to a connection to a new AP for privacy
++ * or other reasons. Note that certain driver specific restrictions
++ * might apply, e.g. no scans in progress, no offchannel operations
++ * in progress, and no active connections.
++ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+@@ -6241,6 +6366,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_BSS_COLOR,
-- NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
+ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
NL80211_EXT_FEATURE_RADAR_BACKGROUND,
++ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
/* add new features before the definition below */
-@@ -7548,7 +7403,7 @@ enum nl80211_sar_specs_attrs {
- * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
- * to advertise the maximum profile periodicity supported by the driver
- * if EMA is enabled. Driver should indicate EMA support to the userspace
-- * by setting wiphy->ema_max_profile_periodicity to
-+ * by setting wiphy->mbssid_max_ema_profile_periodicity to
- * a non-zero value.
- *
- * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
-@@ -7567,7 +7422,7 @@ enum nl80211_sar_specs_attrs {
- *
- * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
- * Setting this flag is permitted only if the driver advertises EMA support
-- * by setting wiphy->ema_max_profile_periodicity to non-zero.
-+ * by setting wiphy->mbssid_max_ema_profile_periodicity to non-zero.
- *
- * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
- * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
-@@ -7586,20 +7441,4 @@ enum nl80211_mbssid_config_attributes {
- NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
- };
-
--/**
-- * enum nl80211_ap_settings_flags - AP settings flags
-- *
-- * @NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT: AP supports external
-- * authentication.
-- * @NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT: Userspace supports SA Query
-- * procedures offload to driver. If driver advertises
-- * %NL80211_AP_SME_SA_QUERY_OFFLOAD in AP SME features, userspace shall
-- * ignore SA Query procedures and validations when this flag is set by
-- * userspace.
-- */
--enum nl80211_ap_settings_flags {
-- NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT = 1 << 0,
-- NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1,
--};
--
- #endif /* __LINUX_NL80211_H */
---- a/event.c
-+++ b/event.c
-@@ -1292,9 +1292,6 @@ static int print_event(struct nl_msg *ms
- case NL80211_CMD_CH_SWITCH_NOTIFY:
- parse_ch_switch_notify(tb, gnlh->cmd);
- break;
-- case NL80211_CMD_ASSOC_COMEBACK: /* 147 */
-- parse_assoc_comeback(tb, gnlh->cmd);
-- break;
- default:
- printf("unknown event %d (%s)\n",
- gnlh->cmd, command_name(gnlh->cmd));
---- a/info.c
-+++ b/info.c
-@@ -164,7 +164,6 @@ static void ext_feat_print(enum nl80211_
- ext_feat_case(PROT_RANGE_NEGO_AND_MEASURE,
- "support for MFP in range measurement negotiation/procedure");
- ext_feat_case(BSS_COLOR, "BSS coloring support");
-- ext_feat_case(FILS_CRYPTO_OFFLOAD, "FILS crypto offload");
- ext_feat_case(RADAR_BACKGROUND, "Radar background support");
- }
- }
---- a/interface.c
-+++ b/interface.c
-@@ -362,8 +362,6 @@ char *channel_width_name(enum nl80211_ch
- return "5 MHz";
- case NL80211_CHAN_WIDTH_10:
- return "10 MHz";
-- case NL80211_CHAN_WIDTH_320:
-- return "320 MHz";
- default:
- return "unknown";
- }
---- a/util.c
-+++ b/util.c
-@@ -508,7 +508,6 @@ static int parse_freqs(struct chandef *c
- case NL80211_CHAN_WIDTH_40:
- case NL80211_CHAN_WIDTH_80:
- case NL80211_CHAN_WIDTH_160:
-- case NL80211_CHAN_WIDTH_320:
- need_cf1 = true;
- break;
- case NL80211_CHAN_WIDTH_1:
-@@ -626,10 +625,6 @@ int parse_freqchan(struct chandef *chand
- .width = NL80211_CHAN_WIDTH_160,
- .freq1_diff = 0,
- .chantype = -1 },
-- { .name = "320MHz",
-- .width = NL80211_CHAN_WIDTH_320,
-- .freq1_diff = 0,
-- .chantype = -1 },
- };
- const struct chanmode *chanmode_selected = NULL;
- unsigned int freq;
-@@ -1599,48 +1594,6 @@ void print_eht_info(struct nlattr *nl_if
- print_iftype_line(tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES]);
- printf("\n");
-
-- if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]) {
-- len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]);
-- if (len > sizeof(mac_cap))
-- len = sizeof(mac_cap);
-- memcpy(mac_cap,
-- nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]),
-- len);
-- }
--
-- if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) {
-- len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]);
--
-- if (len > sizeof(phy_cap))
-- len = sizeof(phy_cap);
--
-- memcpy(phy_cap,
-- nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]),
-- len);
-- }
--
-- if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]) {
-- len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]);
-- if (len > sizeof(mcs_set))
-- len = sizeof(mcs_set);
-- memcpy(mcs_set,
-- nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]),
-- len);
--
-- // Assume that all parts of the MCS set are present
-- mcs_len = sizeof(mcs_set);
-- }
--
-- if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]) {
-- len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]);
-- if (len > sizeof(ppet))
-- len = sizeof(ppet);
-- memcpy(ppet,
-- nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
-- len);
-- ppet_len = len;
-- }
--
- if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
- len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
-
---- a/reg.c
-+++ b/reg.c
-@@ -210,7 +210,6 @@ static int print_reg_handler(struct nl_m
- PARSE_FLAG(NL80211_RRF_NO_80MHZ, "NO-80MHZ");
- PARSE_FLAG(NL80211_RRF_NO_160MHZ, "NO-160MHZ");
- PARSE_FLAG(NL80211_RRF_NO_HE, "NO-HE");
-- PARSE_FLAG(NL80211_RRF_NO_320MHZ, "NO-320MHZ");
-
- /* Kernels that support NO_IR always turn on both flags */
- if ((flags & NL80211_RRF_NO_IR) && (flags & __NL80211_RRF_NO_IBSS)) {
---- a/station.c
-+++ b/station.c
-@@ -239,8 +239,6 @@ void parse_bitrate(struct nlattr *bitrat
- pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
- if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
- pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
-- if (rinfo[NL80211_RATE_INFO_320_MHZ_WIDTH])
-- pos += snprintf(pos, buflen - (pos - buf), " 320MHz");
- if (rinfo[NL80211_RATE_INFO_SHORT_GI])
- pos += snprintf(pos, buflen - (pos - buf), " short GI");
- if (rinfo[NL80211_RATE_INFO_VHT_NSS])
-@@ -261,18 +259,6 @@ void parse_bitrate(struct nlattr *bitrat
- if (rinfo[NL80211_RATE_INFO_HE_RU_ALLOC])
- pos += snprintf(pos, buflen - (pos - buf),
- " HE-RU-ALLOC %d", nla_get_u8(rinfo[NL80211_RATE_INFO_HE_RU_ALLOC]));
-- if (rinfo[NL80211_RATE_INFO_EHT_MCS])
-- pos += snprintf(pos, buflen - (pos - buf),
-- " EHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_MCS]));
-- if (rinfo[NL80211_RATE_INFO_EHT_NSS])
-- pos += snprintf(pos, buflen - (pos - buf),
-- " EHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_NSS]));
-- if (rinfo[NL80211_RATE_INFO_EHT_GI])
-- pos += snprintf(pos, buflen - (pos - buf),
-- " EHT-GI %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_GI]));
-- if (rinfo[NL80211_RATE_INFO_EHT_RU_ALLOC])
-- pos += snprintf(pos, buflen - (pos - buf),
-- " EHT-RU-ALLOC %d", nla_get_u8(rinfo[NL80211_RATE_INFO_EHT_RU_ALLOC]));
- }
-
- static char *get_chain_signal(struct nlattr *attr_list)
+ NUM_NL80211_EXT_FEATURES,
case NL80211_CMD_JOIN_IBSS:
mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC]));
printf("IBSS %s joined\n", macbuf);
-@@ -1292,9 +1295,9 @@ static int print_event(struct nl_msg *ms
- case NL80211_CMD_CH_SWITCH_NOTIFY:
- parse_ch_switch_notify(tb, gnlh->cmd);
+@@ -1295,9 +1298,9 @@ static int print_event(struct nl_msg *ms
+ case NL80211_CMD_ASSOC_COMEBACK: /* 147 */
+ parse_assoc_comeback(tb, gnlh->cmd);
break;
+#endif
default:
--- a/info.c
+++ b/info.c
-@@ -308,6 +308,7 @@ next:
+@@ -309,6 +309,7 @@ next:
}
}
if (tb_band[NL80211_BAND_ATTR_RATES]) {
printf("\t\tBitrates (non-HT):\n");
nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
-@@ -324,6 +325,7 @@ next:
+@@ -325,6 +326,7 @@ next:
printf("\n");
}
}
}
}
-@@ -389,6 +391,7 @@ next:
+@@ -390,6 +392,7 @@ next:
printf("\tCoverage class: %d (up to %dm)\n", coverage, 450 * coverage);
}
if (tb_msg[NL80211_ATTR_CIPHER_SUITES]) {
int num = nla_len(tb_msg[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
int i;
-@@ -400,6 +403,7 @@ next:
+@@ -401,6 +404,7 @@ next:
cipher_name(ciphers[i]));
}
}
if (tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX] &&
tb_msg[NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX])
-@@ -417,9 +421,11 @@ next:
+@@ -418,9 +422,11 @@ next:
print_iftype_list("\tSupported interface modes", "\t\t",
tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]);
if (tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS]) {
struct nlattr *nl_combi;
-@@ -509,6 +515,7 @@ broken_combination:
+@@ -510,6 +516,7 @@ broken_combination:
printf("\tinterface combinations are not supported\n");
}
if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) {
printf("\tSupported commands:\n");
nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], rem_cmd)
-@@ -606,6 +613,7 @@ broken_combination:
+@@ -607,6 +614,7 @@ broken_combination:
printf("\t\t * wake up on TCP connection\n");
}
}
if (tb_msg[NL80211_ATTR_ROAM_SUPPORT])
printf("\tDevice supports roaming.\n");
-@@ -644,6 +652,7 @@ broken_combination:
+@@ -645,6 +653,7 @@ broken_combination:
}
}
if (tb_msg[NL80211_ATTR_FEATURE_FLAGS]) {
unsigned int features = nla_get_u32(tb_msg[NL80211_ATTR_FEATURE_FLAGS]);
-@@ -708,6 +717,7 @@ broken_combination:
+@@ -709,6 +718,7 @@ broken_combination:
if (features & NL80211_FEATURE_ND_RANDOM_MAC_ADDR)
printf("\tDevice supports randomizing MAC-addr in net-detect scans.\n");
}
if (tb_msg[NL80211_ATTR_TDLS_SUPPORT])
printf("\tDevice supports T-DLS.\n");
-@@ -773,6 +783,7 @@ TOPLEVEL(list, NULL, NL80211_CMD_GET_WIP
+@@ -774,6 +784,7 @@ TOPLEVEL(list, NULL, NL80211_CMD_GET_WIP
"List all wireless devices and their capabilities.");
TOPLEVEL(phy, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info, NULL);
static int handle_commands(struct nl80211_state *state, struct nl_msg *msg,
int argc, char **argv, enum id_input id)
{
-@@ -784,6 +795,7 @@ static int handle_commands(struct nl8021
+@@ -785,6 +796,7 @@ static int handle_commands(struct nl8021
}
TOPLEVEL(commands, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_NONE, handle_commands,
"list all known commands and their decimal & hex value");
ifeq ($(NO_PKG_CONFIG),)
--- a/station.c
+++ b/station.c
-@@ -777,10 +777,12 @@ static int handle_station_set_plink(stru
+@@ -791,10 +791,12 @@ static int handle_station_set_plink(stru
nla_put_failure:
return -ENOBUFS;
}
static int handle_station_set_vlan(struct nl80211_state *state,
struct nl_msg *msg,
-@@ -875,11 +877,13 @@ static int handle_station_set_mesh_power
+@@ -889,11 +891,13 @@ static int handle_station_set_mesh_power
nla_put_failure:
return -ENOBUFS;
}
struct nl_msg *msg,
--- a/interface.c
+++ b/interface.c
-@@ -627,9 +627,11 @@ static int handle_interface_wds_peer(str
+@@ -629,9 +629,11 @@ static int handle_interface_wds_peer(str
nla_put_failure:
return -ENOBUFS;
}
static int set_mcast_rate(struct nl80211_state *state,
struct nl_msg *msg,
-@@ -719,6 +721,7 @@ static int handle_chan(struct nl80211_st
+@@ -721,6 +723,7 @@ static int handle_chan(struct nl80211_st
return handle_chanfreq(state, msg, true, argc, argv, id);
}
SECTION(switch);
COMMAND(switch, freq,
"<freq> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] [beacons <count>] [block-tx]\n"
-@@ -990,3 +993,4 @@ COMMAND(set, tidconf, "[peer <MAC addres
+@@ -992,3 +995,4 @@ COMMAND(set, tidconf, "[peer <MAC addres
" $ iw dev wlan0 set tidconf peer xx:xx:xx:xx:xx:xx tids 0x2 bitrates auto\n"
" $ iw dev wlan0 set tidconf peer xx:xx:xx:xx:xx:xx tids 0x2 bitrates limit vht-mcs-5 4:9\n"
);