summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath10k/usb.c8
-rw-r--r--drivers/net/wireless/ath/ath12k/ahb.h4
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h2
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_htt.c24
-rw-r--r--drivers/net/wireless/ath/ath12k/hal.h31
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c67
-rw-r--r--drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c76
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c58
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.h14
-rw-r--r--drivers/net/wireless/ath/ath6kl/usb.c16
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h11
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c4
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.c18
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/bz.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/dr.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/sc.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.c132
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.h28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h167
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/power.h37
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h45
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/stats.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dbg.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/dump.c69
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.c32
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.h8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c151
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.c236
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/uefi.h141
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c23
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/fw.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.h15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/low_latency.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c52
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/nan.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/phy.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.c5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ptp.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.c178
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/regulatory.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.c25
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.c224
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/scan.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.c50
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/stats.c31
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.c78
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tlc.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c157
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c18
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c132
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c291
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ptp.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tdls.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/channel.c39
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c33
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/eeprom.c154
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c230
-rw-r--r--drivers/net/wireless/mediatek/mt76/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h47
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.c15
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c47
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c28
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c46
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h15
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/main.c9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c66
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/init.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c29
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci.c70
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c390
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c194
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.h7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regd.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x.h7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_core.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_dma.c18
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_regs.h6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_usb.c51
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c36
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/dma.c204
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c64
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c110
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c161
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.h5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c417
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c823
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h112
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h70
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/npu.c465
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/npu.c37
-rw-r--r--drivers/net/wireless/mediatek/mt76/scan.c70
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c34
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/mcu.c15
-rw-r--r--drivers/net/wireless/mediatek/mt7601u/usb.h1
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim.c35
-rw-r--r--include/linux/ieee80211-nan.h7
-rw-r--r--include/linux/ieee80211.h7
-rw-r--r--include/net/cfg80211.h265
-rw-r--r--include/uapi/linux/nl80211.h232
-rw-r--r--net/mac80211/cfg.c4
-rw-r--r--net/mac80211/chan.c199
-rw-r--r--net/mac80211/he.c37
-rw-r--r--net/mac80211/ht.c6
-rw-r--r--net/mac80211/ibss.c4
-rw-r--r--net/mac80211/ieee80211_i.h12
-rw-r--r--net/mac80211/iface.c28
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/mlme.c17
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac80211/trace.h5
-rw-r--r--net/mac80211/util.c1
-rw-r--r--net/mac80211/vht.c33
-rw-r--r--net/wireless/chan.c6
-rw-r--r--net/wireless/core.c130
-rw-r--r--net/wireless/core.h10
-rw-r--r--net/wireless/mlme.c13
-rw-r--r--net/wireless/nl80211.c905
-rw-r--r--net/wireless/rdev-ops.h32
-rw-r--r--net/wireless/reg.c27
-rw-r--r--net/wireless/sysfs.c27
-rw-r--r--net/wireless/trace.h105
-rw-r--r--net/wireless/util.c28
166 files changed, 7113 insertions, 2303 deletions
diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c
index 6661fff326e0..987d57a01ddf 100644
--- a/drivers/net/wireless/ath/ath10k/usb.c
+++ b/drivers/net/wireless/ath/ath10k/usb.c
@@ -1016,7 +1016,6 @@ static int ath10k_usb_probe(struct usb_interface *interface,
netif_napi_add(ar->napi_dev, &ar->napi, ath10k_usb_napi_poll);
- usb_get_dev(dev);
vendor_id = le16_to_cpu(dev->descriptor.idVendor);
product_id = le16_to_cpu(dev->descriptor.idProduct);
@@ -1055,12 +1054,10 @@ err_usb_destroy:
err:
ath10k_core_destroy(ar);
- usb_put_dev(dev);
-
return ret;
}
-static void ath10k_usb_remove(struct usb_interface *interface)
+static void ath10k_usb_disconnect(struct usb_interface *interface)
{
struct ath10k_usb *ar_usb;
@@ -1071,7 +1068,6 @@ static void ath10k_usb_remove(struct usb_interface *interface)
ath10k_core_unregister(ar_usb->ar);
netif_napi_del(&ar_usb->ar->napi);
ath10k_usb_destroy(ar_usb->ar);
- usb_put_dev(interface_to_usbdev(interface));
ath10k_core_destroy(ar_usb->ar);
}
@@ -1117,7 +1113,7 @@ static struct usb_driver ath10k_usb_driver = {
.probe = ath10k_usb_probe,
.suspend = ath10k_usb_pm_suspend,
.resume = ath10k_usb_pm_resume,
- .disconnect = ath10k_usb_remove,
+ .disconnect = ath10k_usb_disconnect,
.id_table = ath10k_usb_ids,
.supports_autosuspend = true,
.disable_hub_initiated_lpm = 1,
diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h
index 8a040d03d27a..be9e31b3682d 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.h
+++ b/drivers/net/wireless/ath/ath12k/ahb.h
@@ -21,8 +21,8 @@
#define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ)
#define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP
#define ATH12K_AHB_FW_PREFIX "q6_fw"
-#define ATH12K_AHB_FW_SUFFIX ".mdt"
-#define ATH12K_AHB_FW2 "iu_fw.mdt"
+#define ATH12K_AHB_FW_SUFFIX ".mbn"
+#define ATH12K_AHB_FW2 "iu_fw.mbn"
#define ATH12K_AHB_UPD_SWID 0x12
#define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ)
#define ATH12K_USERPD_READY_TIMEOUT (10 * HZ)
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 760c76d6f0f4..59c193b24764 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -523,7 +523,7 @@ struct ath12k_sta {
u16 links_map;
u8 assoc_link_id;
u16 ml_peer_id;
- u8 num_peer;
+ u16 free_logical_link_idx_map;
enum ieee80211_sta_state state;
};
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c
index e71bb71a6020..9c19d9707abf 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.c
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.c
@@ -205,16 +205,9 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev,
if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE)))
return;
- if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) {
+ if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON))
is_ampdu =
HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags);
- tx_retry_failed =
- __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) -
- __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success);
- tx_retry_count =
- HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) +
- HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags);
- }
if (usr_stats->tlv_flags &
BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) {
@@ -223,10 +216,19 @@ ath12k_update_per_peer_tx_stats(struct ath12k_pdev_dp *dp_pdev,
HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M);
tid = le32_get_bits(usr_stats->ack_ba.info,
HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM);
- }
- if (common->fes_duration_us)
- tx_duration = le32_to_cpu(common->fes_duration_us);
+ if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) {
+ tx_retry_failed =
+ __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_tried) -
+ __le16_to_cpu(usr_stats->cmpltn_cmn.mpdu_success);
+ tx_retry_count =
+ HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) +
+ HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags);
+ }
+
+ if (common->fes_duration_us)
+ tx_duration = le32_to_cpu(common->fes_duration_us);
+ }
user_rate = &usr_stats->rate;
flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags);
diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h
index 43e3880f8257..bf4f7dbae866 100644
--- a/drivers/net/wireless/ath/ath12k/hal.h
+++ b/drivers/net/wireless/ath/ath12k/hal.h
@@ -268,21 +268,28 @@ enum hal_rx_reception_type {
};
enum hal_rx_legacy_rate {
- HAL_RX_LEGACY_RATE_1_MBPS,
- HAL_RX_LEGACY_RATE_2_MBPS,
- HAL_RX_LEGACY_RATE_5_5_MBPS,
- HAL_RX_LEGACY_RATE_6_MBPS,
- HAL_RX_LEGACY_RATE_9_MBPS,
- HAL_RX_LEGACY_RATE_11_MBPS,
- HAL_RX_LEGACY_RATE_12_MBPS,
- HAL_RX_LEGACY_RATE_18_MBPS,
- HAL_RX_LEGACY_RATE_24_MBPS,
- HAL_RX_LEGACY_RATE_36_MBPS,
- HAL_RX_LEGACY_RATE_48_MBPS,
- HAL_RX_LEGACY_RATE_54_MBPS,
+ HAL_RX_LEGACY_RATE_LP_1_MBPS,
+ HAL_RX_LEGACY_RATE_LP_2_MBPS,
+ HAL_RX_LEGACY_RATE_LP_5_5_MBPS,
+ HAL_RX_LEGACY_RATE_LP_11_MBPS,
+ HAL_RX_LEGACY_RATE_SP_2_MBPS,
+ HAL_RX_LEGACY_RATE_SP_5_5_MBPS,
+ HAL_RX_LEGACY_RATE_SP_11_MBPS,
HAL_RX_LEGACY_RATE_INVALID,
};
+enum hal_rx_legacy_rates_ofdm {
+ HAL_RX_LEGACY_RATE_OFDM_48_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_24_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_12_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_6_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_54_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_36_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_18_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_9_MBPS,
+ HAL_RX_LEGACY_RATE_OFDM_INVALID,
+};
+
enum hal_ring_type {
HAL_REO_DST,
HAL_REO_EXCEPTION,
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index a03881c73d68..553ec28b6aaa 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -164,30 +164,31 @@ static const struct ieee80211_channel ath12k_6ghz_channels[] = {
CHAN6G(233, 7115, 0),
};
+#define ATH12K_MAC_RATE_A_M(bps, code) \
+ { .bitrate = (bps), .hw_value = (code),\
+ .flags = IEEE80211_RATE_MANDATORY_A }
+
+#define ATH12K_MAC_RATE_B(bps, code, code_short) \
+ { .bitrate = (bps), .hw_value = (code), .hw_value_short = (code_short),\
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE }
+
static struct ieee80211_rate ath12k_legacy_rates[] = {
{ .bitrate = 10,
.hw_value = ATH12K_HW_RATE_CCK_LP_1M },
- { .bitrate = 20,
- .hw_value = ATH12K_HW_RATE_CCK_LP_2M,
- .hw_value_short = ATH12K_HW_RATE_CCK_SP_2M,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 55,
- .hw_value = ATH12K_HW_RATE_CCK_LP_5_5M,
- .hw_value_short = ATH12K_HW_RATE_CCK_SP_5_5M,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 110,
- .hw_value = ATH12K_HW_RATE_CCK_LP_11M,
- .hw_value_short = ATH12K_HW_RATE_CCK_SP_11M,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
-
- { .bitrate = 60, .hw_value = ATH12K_HW_RATE_OFDM_6M },
- { .bitrate = 90, .hw_value = ATH12K_HW_RATE_OFDM_9M },
- { .bitrate = 120, .hw_value = ATH12K_HW_RATE_OFDM_12M },
- { .bitrate = 180, .hw_value = ATH12K_HW_RATE_OFDM_18M },
- { .bitrate = 240, .hw_value = ATH12K_HW_RATE_OFDM_24M },
- { .bitrate = 360, .hw_value = ATH12K_HW_RATE_OFDM_36M },
- { .bitrate = 480, .hw_value = ATH12K_HW_RATE_OFDM_48M },
- { .bitrate = 540, .hw_value = ATH12K_HW_RATE_OFDM_54M },
+ ATH12K_MAC_RATE_B(20, ATH12K_HW_RATE_CCK_LP_2M,
+ ATH12K_HW_RATE_CCK_SP_2M),
+ ATH12K_MAC_RATE_B(55, ATH12K_HW_RATE_CCK_LP_5_5M,
+ ATH12K_HW_RATE_CCK_SP_5_5M),
+ ATH12K_MAC_RATE_B(110, ATH12K_HW_RATE_CCK_LP_11M,
+ ATH12K_HW_RATE_CCK_SP_11M),
+ ATH12K_MAC_RATE_A_M(60, ATH12K_HW_RATE_OFDM_6M),
+ ATH12K_MAC_RATE_A_M(90, ATH12K_HW_RATE_OFDM_9M),
+ ATH12K_MAC_RATE_A_M(120, ATH12K_HW_RATE_OFDM_12M),
+ ATH12K_MAC_RATE_A_M(180, ATH12K_HW_RATE_OFDM_18M),
+ ATH12K_MAC_RATE_A_M(240, ATH12K_HW_RATE_OFDM_24M),
+ ATH12K_MAC_RATE_A_M(360, ATH12K_HW_RATE_OFDM_36M),
+ ATH12K_MAC_RATE_A_M(480, ATH12K_HW_RATE_OFDM_48M),
+ ATH12K_MAC_RATE_A_M(540, ATH12K_HW_RATE_OFDM_54M),
};
static const int
@@ -732,11 +733,17 @@ u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
if (ath12k_mac_bitrate_is_cck(rate->bitrate) != cck)
continue;
- if (rate->hw_value == hw_rate)
+ /* To handle 802.11a PPDU type */
+ if ((!cck) && (rate->hw_value == hw_rate) &&
+ (rate->flags & IEEE80211_RATE_MANDATORY_A))
return i;
+ /* To handle 802.11b short PPDU type */
else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
rate->hw_value_short == hw_rate)
return i;
+ /* To handle 802.11b long PPDU type */
+ else if (rate->hw_value == hw_rate)
+ return i;
}
return 0;
@@ -6786,6 +6793,8 @@ static void ath12k_mac_free_unassign_link_sta(struct ath12k_hw *ah,
return;
ahsta->links_map &= ~BIT(link_id);
+ ahsta->free_logical_link_idx_map |= BIT(arsta->link_idx);
+
rcu_assign_pointer(ahsta->link[link_id], NULL);
synchronize_rcu();
@@ -7104,6 +7113,7 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah,
struct ieee80211_sta *sta = ath12k_ahsta_to_sta(ahsta);
struct ieee80211_link_sta *link_sta;
struct ath12k_link_vif *arvif;
+ int link_idx;
lockdep_assert_wiphy(ah->hw->wiphy);
@@ -7122,8 +7132,16 @@ static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah,
ether_addr_copy(arsta->addr, link_sta->addr);
- /* logical index of the link sta in order of creation */
- arsta->link_idx = ahsta->num_peer++;
+ if (!ahsta->free_logical_link_idx_map)
+ return -ENOSPC;
+
+ /*
+ * Allocate a logical link index by selecting the first available bit
+ * from the free logical index map
+ */
+ link_idx = __ffs(ahsta->free_logical_link_idx_map);
+ ahsta->free_logical_link_idx_map &= ~BIT(link_idx);
+ arsta->link_idx = link_idx;
arsta->link_id = link_id;
ahsta->links_map |= BIT(arsta->link_id);
@@ -7632,6 +7650,7 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
memset(ahsta, 0, sizeof(*ahsta));
+ ahsta->free_logical_link_idx_map = U16_MAX;
arsta = &ahsta->deflink;
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
index c9cea597a92e..77f5d23be78d 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
@@ -405,6 +405,42 @@ ath12k_wifi7_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *r
}
}
+static __always_inline u8
+ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(u8 rate)
+{
+ u8 ath12k_rate;
+
+ /* Map hal_rx_legacy_rate to ath12k_hw_rate_cck */
+ switch (rate) {
+ case HAL_RX_LEGACY_RATE_LP_1_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_LP_1M;
+ break;
+ case HAL_RX_LEGACY_RATE_LP_2_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_LP_2M;
+ break;
+ case HAL_RX_LEGACY_RATE_LP_5_5_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_LP_5_5M;
+ break;
+ case HAL_RX_LEGACY_RATE_LP_11_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_LP_11M;
+ break;
+ case HAL_RX_LEGACY_RATE_SP_2_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_SP_2M;
+ break;
+ case HAL_RX_LEGACY_RATE_SP_5_5_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_SP_5_5M;
+ break;
+ case HAL_RX_LEGACY_RATE_SP_11_MBPS:
+ ath12k_rate = ATH12K_HW_RATE_CCK_SP_11M;
+ break;
+ default:
+ ath12k_rate = rate;
+ break;
+ }
+
+ return ath12k_rate;
+}
+
static void
ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
struct hal_rx_mon_ppdu_info *ppdu_info)
@@ -415,25 +451,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
rate = u32_get_bits(info0, HAL_RX_LSIG_B_INFO_INFO0_RATE);
switch (rate) {
case 1:
- rate = HAL_RX_LEGACY_RATE_1_MBPS;
+ rate = HAL_RX_LEGACY_RATE_LP_1_MBPS;
break;
case 2:
- case 5:
- rate = HAL_RX_LEGACY_RATE_2_MBPS;
+ rate = HAL_RX_LEGACY_RATE_LP_2_MBPS;
break;
case 3:
- case 6:
- rate = HAL_RX_LEGACY_RATE_5_5_MBPS;
+ rate = HAL_RX_LEGACY_RATE_LP_5_5_MBPS;
break;
case 4:
+ rate = HAL_RX_LEGACY_RATE_LP_11_MBPS;
+ break;
+ case 5:
+ rate = HAL_RX_LEGACY_RATE_SP_2_MBPS;
+ break;
+ case 6:
+ rate = HAL_RX_LEGACY_RATE_SP_5_5_MBPS;
+ break;
case 7:
- rate = HAL_RX_LEGACY_RATE_11_MBPS;
+ rate = HAL_RX_LEGACY_RATE_SP_11_MBPS;
break;
default:
rate = HAL_RX_LEGACY_RATE_INVALID;
+ break;
}
- ppdu_info->rate = rate;
+ ppdu_info->rate = ath12k_wifi7_hal_mon_map_legacy_rate_to_hw_rate(rate);
ppdu_info->cck_flag = 1;
}
@@ -447,31 +490,32 @@ ath12k_wifi7_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga,
rate = u32_get_bits(info0, HAL_RX_LSIG_A_INFO_INFO0_RATE);
switch (rate) {
case 8:
- rate = HAL_RX_LEGACY_RATE_48_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_48_MBPS;
break;
case 9:
- rate = HAL_RX_LEGACY_RATE_24_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_24_MBPS;
break;
case 10:
- rate = HAL_RX_LEGACY_RATE_12_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_12_MBPS;
break;
case 11:
- rate = HAL_RX_LEGACY_RATE_6_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_6_MBPS;
break;
case 12:
- rate = HAL_RX_LEGACY_RATE_54_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_54_MBPS;
break;
case 13:
- rate = HAL_RX_LEGACY_RATE_36_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_36_MBPS;
break;
case 14:
- rate = HAL_RX_LEGACY_RATE_18_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_18_MBPS;
break;
case 15:
- rate = HAL_RX_LEGACY_RATE_9_MBPS;
+ rate = HAL_RX_LEGACY_RATE_OFDM_9_MBPS;
break;
default:
- rate = HAL_RX_LEGACY_RATE_INVALID;
+ rate = HAL_RX_LEGACY_RATE_OFDM_INVALID;
+ break;
}
ppdu_info->rate = rate;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index b93e33edf369..65a05a9520ff 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -10017,50 +10017,46 @@ static int ath12k_connect_pdev_htc_service(struct ath12k_base *ab,
static int
ath12k_wmi_send_unit_test_cmd(struct ath12k *ar,
- struct wmi_unit_test_cmd ut_cmd,
- u32 *test_args)
+ const struct wmi_unit_test_arg *ut)
{
struct ath12k_wmi_pdev *wmi = ar->wmi;
struct wmi_unit_test_cmd *cmd;
+ int buf_len, arg_len;
struct sk_buff *skb;
struct wmi_tlv *tlv;
+ __le32 *ut_cmd_args;
void *ptr;
- u32 *ut_cmd_args;
- int buf_len, arg_len;
int ret;
int i;
- arg_len = sizeof(u32) * le32_to_cpu(ut_cmd.num_args);
- buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE;
+ arg_len = sizeof(*ut_cmd_args) * ut->num_args;
+ buf_len = sizeof(*cmd) + arg_len + TLV_HDR_SIZE;
skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
if (!skb)
return -ENOMEM;
- cmd = (struct wmi_unit_test_cmd *)skb->data;
+ ptr = skb->data;
+ cmd = ptr;
cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_UNIT_TEST_CMD,
- sizeof(ut_cmd));
-
- cmd->vdev_id = ut_cmd.vdev_id;
- cmd->module_id = ut_cmd.module_id;
- cmd->num_args = ut_cmd.num_args;
- cmd->diag_token = ut_cmd.diag_token;
-
- ptr = skb->data + sizeof(ut_cmd);
+ sizeof(*cmd));
+ cmd->vdev_id = cpu_to_le32(ut->vdev_id);
+ cmd->module_id = cpu_to_le32(ut->module_id);
+ cmd->num_args = cpu_to_le32(ut->num_args);
+ cmd->diag_token = cpu_to_le32(ut->diag_token);
+ ptr += sizeof(*cmd);
tlv = ptr;
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, arg_len);
ptr += TLV_HDR_SIZE;
-
ut_cmd_args = ptr;
- for (i = 0; i < le32_to_cpu(ut_cmd.num_args); i++)
- ut_cmd_args[i] = test_args[i];
+ for (i = 0; i < ut->num_args; i++)
+ ut_cmd_args[i] = cpu_to_le32(ut->args[i]);
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"WMI unit test : module %d vdev %d n_args %d token %d\n",
- cmd->module_id, cmd->vdev_id, cmd->num_args,
- cmd->diag_token);
+ ut->module_id, ut->vdev_id, ut->num_args, ut->diag_token);
ret = ath12k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID);
@@ -10076,8 +10072,7 @@ ath12k_wmi_send_unit_test_cmd(struct ath12k *ar,
int ath12k_wmi_simulate_radar(struct ath12k *ar)
{
struct ath12k_link_vif *arvif;
- u32 dfs_args[DFS_MAX_TEST_ARGS];
- struct wmi_unit_test_cmd wmi_ut;
+ struct wmi_unit_test_arg wmi_ut = {};
bool arvif_found = false;
list_for_each_entry(arvif, &ar->arvifs, list) {
@@ -10090,22 +10085,23 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar)
if (!arvif_found)
return -EINVAL;
- dfs_args[DFS_TEST_CMDID] = 0;
- dfs_args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id;
- /* Currently we could pass segment_id(b0 - b1), chirp(b2)
+ wmi_ut.args[DFS_TEST_CMDID] = 0;
+ wmi_ut.args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id;
+ /*
+ * Currently we could pass segment_id(b0 - b1), chirp(b2)
* freq offset (b3 - b10) to unit test. For simulation
* purpose this can be set to 0 which is valid.
*/
- dfs_args[DFS_TEST_RADAR_PARAM] = 0;
+ wmi_ut.args[DFS_TEST_RADAR_PARAM] = 0;
- wmi_ut.vdev_id = cpu_to_le32(arvif->vdev_id);
- wmi_ut.module_id = cpu_to_le32(DFS_UNIT_TEST_MODULE);
- wmi_ut.num_args = cpu_to_le32(DFS_MAX_TEST_ARGS);
- wmi_ut.diag_token = cpu_to_le32(DFS_UNIT_TEST_TOKEN);
+ wmi_ut.vdev_id = arvif->vdev_id;
+ wmi_ut.module_id = DFS_UNIT_TEST_MODULE;
+ wmi_ut.num_args = DFS_MAX_TEST_ARGS;
+ wmi_ut.diag_token = DFS_UNIT_TEST_TOKEN;
ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Triggering Radar Simulation\n");
- return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args);
+ return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut);
}
int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 0bf0a7941cd3..5ba9b7d3a888 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -4193,7 +4193,6 @@ struct wmi_addba_clear_resp_cmd {
struct ath12k_wmi_mac_addr_params peer_macaddr;
} __packed;
-#define DFS_PHYERR_UNIT_TEST_CMD 0
#define DFS_UNIT_TEST_MODULE 0x2b
#define DFS_UNIT_TEST_TOKEN 0xAA
@@ -4204,10 +4203,15 @@ enum dfs_test_args_idx {
DFS_MAX_TEST_ARGS,
};
-struct wmi_dfs_unit_test_arg {
- u32 cmd_id;
- u32 pdev_id;
- u32 radar_param;
+/* update if another test command requires more */
+#define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS
+
+struct wmi_unit_test_arg {
+ u32 vdev_id;
+ u32 module_id;
+ u32 diag_token;
+ u32 num_args;
+ u32 args[WMI_UNIT_TEST_ARGS_MAX];
};
struct wmi_unit_test_cmd {
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
index 852e77e41bde..79c18f5ee02b 100644
--- a/drivers/net/wireless/ath/ath6kl/usb.c
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -1124,8 +1124,6 @@ static int ath6kl_usb_probe(struct usb_interface *interface,
int vendor_id, product_id;
int ret = 0;
- usb_get_dev(dev);
-
vendor_id = le16_to_cpu(dev->descriptor.idVendor);
product_id = le16_to_cpu(dev->descriptor.idProduct);
@@ -1143,11 +1141,8 @@ static int ath6kl_usb_probe(struct usb_interface *interface,
ath6kl_dbg(ATH6KL_DBG_USB, "USB 1.1 Host\n");
ar_usb = ath6kl_usb_create(interface);
-
- if (ar_usb == NULL) {
- ret = -ENOMEM;
- goto err_usb_put;
- }
+ if (ar_usb == NULL)
+ return -ENOMEM;
ar = ath6kl_core_create(&ar_usb->udev->dev);
if (ar == NULL) {
@@ -1176,15 +1171,12 @@ err_core_free:
ath6kl_core_destroy(ar);
err_usb_destroy:
ath6kl_usb_destroy(ar_usb);
-err_usb_put:
- usb_put_dev(dev);
return ret;
}
-static void ath6kl_usb_remove(struct usb_interface *interface)
+static void ath6kl_usb_disconnect(struct usb_interface *interface)
{
- usb_put_dev(interface_to_usbdev(interface));
ath6kl_usb_device_detached(interface);
}
@@ -1235,7 +1227,7 @@ static struct usb_driver ath6kl_usb_driver = {
.probe = ath6kl_usb_probe,
.suspend = ath6kl_usb_pm_suspend,
.resume = ath6kl_usb_pm_resume,
- .disconnect = ath6kl_usb_remove,
+ .disconnect = ath6kl_usb_disconnect,
.id_table = ath6kl_usb_ids,
.supports_autosuspend = true,
.disable_hub_initiated_lpm = 1,
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index 3080d82e25cc..8fbece3fdad9 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -1630,16 +1630,6 @@ enum wmi_roam_mode {
WMI_LOCK_BSS_MODE = 3, /* Lock to the current BSS */
};
-struct bss_bias {
- u8 bssid[ETH_ALEN];
- s8 bias;
-} __packed;
-
-struct bss_bias_info {
- u8 num_bss;
- struct bss_bias bss_bias[];
-} __packed;
-
struct low_rssi_scan_params {
__le16 lrssi_scan_period;
a_sle16 lrssi_scan_threshold;
@@ -1652,7 +1642,6 @@ struct roam_ctrl_cmd {
union {
u8 bssid[ETH_ALEN]; /* WMI_FORCE_ROAM */
u8 roam_mode; /* WMI_SET_ROAM_MODE */
- struct bss_bias_info bss; /* WMI_SET_HOST_BIAS */
struct low_rssi_scan_params params; /* WMI_SET_LRSSI_SCAN_PARAMS
*/
} __packed info;
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 8533b88974b2..821909b81ea9 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -1382,8 +1382,6 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
goto err_alloc;
}
- usb_get_dev(udev);
-
hif_dev->udev = udev;
hif_dev->interface = interface;
hif_dev->usb_device_id = id;
@@ -1403,7 +1401,6 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
err_fw_req:
usb_set_intfdata(interface, NULL);
kfree(hif_dev);
- usb_put_dev(udev);
err_alloc:
return ret;
}
@@ -1451,7 +1448,6 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
kfree(hif_dev);
dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n");
- usb_put_dev(udev);
}
#ifdef CONFIG_PM
diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c
index 3a8df7a18042..05da6987a845 100644
--- a/drivers/net/wireless/broadcom/b43/dma.c
+++ b/drivers/net/wireless/broadcom/b43/dma.c
@@ -837,18 +837,19 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
struct b43_dmaring *ring;
int i, err;
dma_addr_t dma_test;
+ size_t nr_slots;
- ring = kzalloc_obj(*ring);
+ if (for_tx)
+ nr_slots = B43_TXRING_SLOTS;
+ else
+ nr_slots = B43_RXRING_SLOTS;
+
+ ring = kzalloc_flex(*ring, meta, nr_slots);
if (!ring)
goto out;
- ring->nr_slots = B43_RXRING_SLOTS;
- if (for_tx)
- ring->nr_slots = B43_TXRING_SLOTS;
+ ring->nr_slots = nr_slots;
- ring->meta = kzalloc_objs(struct b43_dmadesc_meta, ring->nr_slots);
- if (!ring->meta)
- goto err_kfree_ring;
for (i = 0; i < ring->nr_slots; i++)
ring->meta->skb = B43_DMA_PTR_POISON;
@@ -943,8 +944,6 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
err_kfree_txhdr_cache:
kfree(ring->txhdr_cache);
err_kfree_meta:
- kfree(ring->meta);
- err_kfree_ring:
kfree(ring);
ring = NULL;
goto out;
@@ -1004,7 +1003,6 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring,
free_ringmemory(ring);
kfree(ring->txhdr_cache);
- kfree(ring->meta);
kfree(ring);
}
diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h
index c2a357219d4b..f9f65bbe2d76 100644
--- a/drivers/net/wireless/broadcom/b43/dma.h
+++ b/drivers/net/wireless/broadcom/b43/dma.h
@@ -228,8 +228,6 @@ struct b43_dmaring {
const struct b43_dma_ops *ops;
/* Kernel virtual base address of the ring memory. */
void *descbase;
- /* Meta data about all descriptors. */
- struct b43_dmadesc_meta *meta;
/* Cache of TX headers for each TX frame.
* This is to avoid an allocation on each TX.
* This is NULL for an RX ring.
@@ -273,6 +271,8 @@ struct b43_dmaring {
/* Statistics: Total number of TX plus all retries. */
u64 nr_total_packet_tries;
#endif /* CONFIG_B43_DEBUG */
+ /* Meta data about all descriptors. */
+ struct b43_dmadesc_meta meta[] __counted_by(nr_slots);
};
static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index 77db8c75e6e2..3653ddbf3ce9 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_BZ_UCODE_CORE_MAX 101
+#define IWL_BZ_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_BZ_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index a279dcfd3083..83d893b10f8e 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -9,7 +9,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_DR_UCODE_CORE_MAX 101
+#define IWL_DR_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_DR_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index ee00b2af7a1d..749d46dc0236 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_SC_UCODE_CORE_MAX 101
+#define IWL_SC_UCODE_CORE_MAX 102
/* Lowest firmware API version supported */
#define IWL_SC_UCODE_API_MIN 100
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index de9aef0d924c..bf0f851a9075 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
@@ -504,7 +504,8 @@ iwl_acpi_parse_chains_table(union acpi_object *table,
u8 num_chains, u8 num_sub_bands)
{
for (u8 chain = 0; chain < num_chains; chain++) {
- for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;
+ for (u8 subband = 0;
+ subband < ARRAY_SIZE(chains[chain].subbands);
subband++) {
/* if we don't have the values, use the default */
if (subband >= num_sub_bands) {
@@ -534,7 +535,23 @@ int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
- /* start by trying to read revision 2 */
+ /* start by trying to read revision 3 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_WRDS_WIFI_DATA_SIZE_REV3,
+ &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev != 3) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ num_chains = ACPI_SAR_NUM_CHAINS_REV2;
+ num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+
+ goto read_table;
+ }
+
+ /* then try revision 2 */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_WRDS_WIFI_DATA_SIZE_REV2,
&tbl_rev);
@@ -591,6 +608,13 @@ read_table:
goto out_free;
}
+ if (WARN_ON(num_chains * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
flags = wifi_pkg->package.elements[1].integer.value;
@@ -624,7 +648,22 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
- /* start by trying to read revision 2 */
+ /* start by trying to read revision 3 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_EWRD_WIFI_DATA_SIZE_REV3,
+ &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev != 3) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV3;
+
+ goto read_table;
+ }
+
+ /* then try revision 2 */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_EWRD_WIFI_DATA_SIZE_REV2,
&tbl_rev);
@@ -679,6 +718,13 @@ read_table:
goto out_free;
}
+ if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
enabled = !!(wifi_pkg->package.elements[1].integer.value);
n_profiles = wifi_pkg->package.elements[2].integer.value;
@@ -721,6 +767,13 @@ read_table:
if (tbl_rev < 2)
goto set_enabled;
+ if (WARN_ON(ACPI_SAR_NUM_CHAINS_REV0 * 2 * num_sub_bands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains) *
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
/* parse cdb chains for all profiles */
for (i = 0; i < n_profiles; i++) {
struct iwl_sar_profile_chain *chains;
@@ -760,6 +813,12 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
u8 min_profiles;
} rev_data[] = {
{
+ .revisions = BIT(4),
+ .bands = ACPI_GEO_NUM_BANDS_REV4,
+ .profiles = ACPI_NUM_GEO_PROFILES_REV3,
+ .min_profiles = BIOS_GEO_MIN_PROFILE_NUM,
+ },
+ {
.revisions = BIT(3),
.bands = ACPI_GEO_NUM_BANDS_REV2,
.profiles = ACPI_NUM_GEO_PROFILES_REV3,
@@ -812,6 +871,18 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
num_bands = rev_data[idx].bands;
num_profiles = rev_data[idx].profiles;
+ if (WARN_ON(num_profiles >
+ ARRAY_SIZE(fwrt->geo_profiles))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ if (WARN_ON(num_bands >
+ ARRAY_SIZE(fwrt->geo_profiles[0].bands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
if (rev_data[idx].min_profiles) {
/* read header that says # of profiles */
union acpi_object *entry;
@@ -851,18 +922,20 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
read_table:
fwrt->geo_rev = tbl_rev;
+
for (i = 0; i < num_profiles; i++) {
- for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {
+ struct iwl_geo_profile *prof = &fwrt->geo_profiles[i];
+
+ for (j = 0; j < ARRAY_SIZE(prof->bands); j++) {
union acpi_object *entry;
/*
- * num_bands is either 2 or 3, if it's only 2 then
- * fill the third band (6 GHz) with the values from
- * 5 GHz (second band)
+ * num_bands is either 2 or 3 or 4, if it's lower
+ * than 4, fill the third band (6 GHz) with the values
+ * from 5 GHz (second band)
*/
if (j >= num_bands) {
- fwrt->geo_profiles[i].bands[j].max =
- fwrt->geo_profiles[i].bands[1].max;
+ prof->bands[j].max = prof->bands[1].max;
} else {
entry = &wifi_pkg->package.elements[entry_idx];
entry_idx++;
@@ -872,15 +945,17 @@ read_table:
goto out_free;
}
- fwrt->geo_profiles[i].bands[j].max =
+ prof->bands[j].max =
entry->integer.value;
}
- for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {
+ for (k = 0;
+ k < ARRAY_SIZE(prof->bands[0].chains);
+ k++) {
/* same here as above */
if (j >= num_bands) {
- fwrt->geo_profiles[i].bands[j].chains[k] =
- fwrt->geo_profiles[i].bands[1].chains[k];
+ prof->bands[j].chains[k] =
+ prof->bands[1].chains[k];
} else {
entry = &wifi_pkg->package.elements[entry_idx];
entry_idx++;
@@ -890,7 +965,7 @@ read_table:
goto out_free;
}
- fwrt->geo_profiles[i].bands[j].chains[k] =
+ prof->bands[j].chains[k] =
entry->integer.value;
}
}
@@ -898,6 +973,7 @@ read_table:
}
fwrt->geo_num_profiles = num_profiles;
+ fwrt->geo_bios_source = BIOS_SOURCE_ACPI;
fwrt->geo_enabled = true;
ret = 0;
out_free:
@@ -915,6 +991,22 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
if (IS_ERR(data))
return PTR_ERR(data);
+ /* try to read ppag table rev 5 */
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
+ ACPI_PPAG_WIFI_DATA_SIZE_V3, &tbl_rev);
+ if (!IS_ERR(wifi_pkg)) {
+ if (tbl_rev == 5) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V3;
+ IWL_DEBUG_RADIO(fwrt,
+ "Reading PPAG table (tbl_rev=%d)\n",
+ tbl_rev);
+ goto read_table;
+ } else {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ }
+
/* try to read ppag table rev 1 to 4 (all have the same data size) */
wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
@@ -950,6 +1042,15 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
goto out_free;
read_table:
+ if (WARN_ON_ONCE(num_sub_bands >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands))) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ BUILD_BUG_ON(ACPI_PPAG_NUM_CHAINS >
+ ARRAY_SIZE(fwrt->ppag_chains));
+
fwrt->ppag_bios_rev = tbl_rev;
flags = &wifi_pkg->package.elements[1];
@@ -966,7 +1067,7 @@ read_table:
* first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
* following sub-bands to High-Band (5GHz).
*/
- for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
+ for (i = 0; i < ACPI_PPAG_NUM_CHAINS; i++) {
for (j = 0; j < num_sub_bands; j++) {
union acpi_object *ent;
@@ -980,6 +1081,7 @@ read_table:
}
}
+ iwl_bios_print_ppag(fwrt, num_sub_bands);
fwrt->ppag_bios_source = BIOS_SOURCE_ACPI;
ret = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
index 06cece4ea6d9..51a57e57de7a 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
@@ -8,11 +8,6 @@
#include <linux/acpi.h>
#include "fw/regulatory.h"
-#include "fw/api/commands.h"
-#include "fw/api/power.h"
-#include "fw/api/phy.h"
-#include "fw/api/nvm-reg.h"
-#include "fw/api/config.h"
#include "fw/img.h"
#include "iwl-trans.h"
@@ -44,6 +39,7 @@
#define ACPI_SAR_NUM_SUB_BANDS_REV0 5
#define ACPI_SAR_NUM_SUB_BANDS_REV1 11
#define ACPI_SAR_NUM_SUB_BANDS_REV2 11
+#define ACPI_SAR_NUM_SUB_BANDS_REV3 12
#define ACPI_WRDS_WIFI_DATA_SIZE_REV0 (ACPI_SAR_NUM_CHAINS_REV0 * \
ACPI_SAR_NUM_SUB_BANDS_REV0 + 2)
@@ -51,6 +47,8 @@
ACPI_SAR_NUM_SUB_BANDS_REV1 + 2)
#define ACPI_WRDS_WIFI_DATA_SIZE_REV2 (ACPI_SAR_NUM_CHAINS_REV2 * \
ACPI_SAR_NUM_SUB_BANDS_REV2 + 2)
+#define ACPI_WRDS_WIFI_DATA_SIZE_REV3 (ACPI_SAR_NUM_CHAINS_REV2 * \
+ ACPI_SAR_NUM_SUB_BANDS_REV3 + 2)
#define ACPI_EWRD_WIFI_DATA_SIZE_REV0 ((ACPI_SAR_PROFILE_NUM - 1) * \
ACPI_SAR_NUM_CHAINS_REV0 * \
ACPI_SAR_NUM_SUB_BANDS_REV0 + 3)
@@ -60,11 +58,15 @@
#define ACPI_EWRD_WIFI_DATA_SIZE_REV2 ((ACPI_SAR_PROFILE_NUM - 1) * \
ACPI_SAR_NUM_CHAINS_REV2 * \
ACPI_SAR_NUM_SUB_BANDS_REV2 + 3)
+#define ACPI_EWRD_WIFI_DATA_SIZE_REV3 ((ACPI_SAR_PROFILE_NUM - 1) * \
+ ACPI_SAR_NUM_CHAINS_REV2 * \
+ ACPI_SAR_NUM_SUB_BANDS_REV3 + 3)
#define ACPI_WPFC_WIFI_DATA_SIZE 5 /* domain and 4 filter config words */
/* revision 0 and 1 are identical, except for the semantics in the FW */
#define ACPI_GEO_NUM_BANDS_REV0 2
#define ACPI_GEO_NUM_BANDS_REV2 3
+#define ACPI_GEO_NUM_BANDS_REV4 4
#define ACPI_WRDD_WIFI_DATA_SIZE 2
#define ACPI_SPLC_WIFI_DATA_SIZE 2
@@ -96,10 +98,18 @@
*/
#define ACPI_WTAS_WIFI_DATA_SIZE (3 + IWL_WTAS_BLACK_LIST_MAX)
-#define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((IWL_NUM_CHAIN_LIMITS * \
- IWL_NUM_SUB_BANDS_V1) + 2)
-#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((IWL_NUM_CHAIN_LIMITS * \
- IWL_NUM_SUB_BANDS_V2) + 2)
+#define ACPI_PPAG_NUM_CHAINS 2
+#define ACPI_PPAG_NUM_BANDS_V1 5
+#define ACPI_PPAG_NUM_BANDS_V2 11
+#define ACPI_PPAG_NUM_BANDS_V3 12
+#define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V1) + 2)
+#define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V2) + 2)
+
+/* used for ACPI PPAG table rev 5 */
+#define ACPI_PPAG_WIFI_DATA_SIZE_V3 ((ACPI_PPAG_NUM_CHAINS * \
+ ACPI_PPAG_NUM_BANDS_V3) + 2)
#define IWL_SAR_ENABLE_MSK BIT(0)
#define IWL_REDUCE_POWER_FLAGS_POS 1
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index 6a6e11a57dbf..06370c161fe4 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -56,7 +56,8 @@ enum iwl_data_path_subcmd_ids {
RFH_QUEUE_CONFIG_CMD = 0xD,
/**
- * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4
+ * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or
+ * &struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd.
*/
TLC_MNG_CONFIG_CMD = 0xF,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index c7a833f8041a..b398c582b867 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -26,7 +26,7 @@ enum iwl_mac_conf_subcmd_ids {
*/
MISSED_VAP_NOTIF = 0xFA,
/**
- * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd
+ * @SESSION_PROTECTION_CMD: &struct iwl_session_prot_cmd
*/
SESSION_PROTECTION_CMD = 0x5,
/**
@@ -34,7 +34,8 @@ enum iwl_mac_conf_subcmd_ids {
*/
CANCEL_CHANNEL_SWITCH_CMD = 0x6,
/**
- * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd
+ * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd_v3 or
+ * &struct iwl_mac_config_cmd
*/
MAC_CONFIG_CMD = 0x8,
/**
@@ -42,7 +43,8 @@ enum iwl_mac_conf_subcmd_ids {
*/
LINK_CONFIG_CMD = 0x9,
/**
- * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd
+ * @STA_CONFIG_CMD: &struct iwl_sta_cfg_cmd_v1,
+ * &struct iwl_sta_cfg_cmd_v2, or &struct iwl_sta_cfg_cmd
*/
STA_CONFIG_CMD = 0xA,
/**
@@ -356,7 +358,7 @@ struct iwl_mac_wifi_gen_support {
} __packed;
/**
- * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ * struct iwl_mac_config_cmd_v3 - command structure to configure MAC contexts in
* MLD API for versions 2 and 3
* ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
*
@@ -375,7 +377,7 @@ struct iwl_mac_wifi_gen_support {
* @client: client mac data
* @p2p_dev: mac data for p2p device
*/
-struct iwl_mac_config_cmd {
+struct iwl_mac_config_cmd_v3 {
__le32 id_and_color;
__le32 action;
/* MAC_CONTEXT_TYPE_API_E */
@@ -393,7 +395,62 @@ struct iwl_mac_config_cmd {
struct iwl_mac_client_data client;
struct iwl_mac_p2p_dev_data p2p_dev;
};
-} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2, _VER_3 */
+
+/**
+ * struct iwl_mac_nan_data - NAN specific MAC data
+ * @ndi_addrs: extra NDI addresses being used
+ * @ndi_addrs_count: number of extra NDI addresses
+ */
+struct iwl_mac_nan_data {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 reserved;
+ } __packed ndi_addrs[2];
+ __le32 ndi_addrs_count;
+} __packed; /* MAC_CONTEXT_CONFIG_NAN_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ * MLD API for versions 4
+ * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, see &enum iwl_ctxt_action
+ * @mac_type: one of &enum iwl_mac_types
+ * @local_mld_addr: mld address
+ * @reserved_for_local_mld_addr: reserved
+ * @filter_flags: combination of &enum iwl_mac_config_filter_flags
+ * @wifi_gen_v2: he/eht parameters as in cmd version 2
+ * @wifi_gen: he/eht/uhr parameters as in cmd version 3
+ * @nic_not_ack_enabled: mark that the NIC doesn't support receiving
+ * ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG).
+ * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0
+ * len delim to determine if AGG or single.
+ * @client: client mac data
+ * @p2p_dev: mac data for p2p device
+ * @nan: NAN specific data (NAN data interface addresses)
+ */
+struct iwl_mac_config_cmd {
+ __le32 id_and_color;
+ __le32 action;
+ /* MAC_CONTEXT_TYPE_API_E */
+ __le32 mac_type;
+ u8 local_mld_addr[6];
+ __le16 reserved_for_local_mld_addr;
+ __le32 filter_flags;
+ union {
+ struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2;
+ struct iwl_mac_wifi_gen_support wifi_gen;
+ };
+ __le32 nic_not_ack_enabled;
+ /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_3 */
+ union {
+ struct iwl_mac_client_data client;
+ struct iwl_mac_p2p_dev_data p2p_dev;
+ struct iwl_mac_nan_data nan;
+ };
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_4 */
/**
* enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being
@@ -652,6 +709,7 @@ struct iwl_link_config_cmd {
*/
#define IWL_FW_MAX_ACTIVE_LINKS_NUM 2
#define IWL_FW_MAX_LINK_ID 3
+#define IWL_FW_MAX_LINKS IWL_FW_MAX_LINK_ID + 1
/**
* enum iwl_fw_sta_type - FW station types
@@ -662,6 +720,13 @@ struct iwl_link_config_cmd {
* @STATION_TYPE_MCAST: the station used for BCAST / MCAST in GO. Will be
* suspended / resumed at the right timing depending on the clients'
* power save state and the DTIM timing
+ * @STATION_TYPE_NAN_PEER_NMI: NAN management peer station type. A station
+ * of this type can have any number of links (even none) set in the
+ * link_mask. (Supported since version 3.)
+ * @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
+ * of this type can have any number of links (even none) set in the
+ * link_mask. (Supported since version 3.)
+ * @STATION_TYPE_MAX: maximum number of FW station types
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
* for the aux sta, so this type is only for driver - internal use.
*/
@@ -669,8 +734,11 @@ enum iwl_fw_sta_type {
STATION_TYPE_PEER,
STATION_TYPE_BCAST_MGMT,
STATION_TYPE_MCAST,
- STATION_TYPE_AUX,
-}; /* STATION_TYPE_E_VER_1 */
+ STATION_TYPE_NAN_PEER_NMI,
+ STATION_TYPE_NAN_PEER_NDI,
+ STATION_TYPE_MAX,
+ STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
+}; /* STATION_TYPE_E_VER_1, _VER_2 */
/**
* struct iwl_sta_cfg_cmd_v1 - cmd structure to add a peer sta to the uCode's
@@ -729,7 +797,7 @@ struct iwl_sta_cfg_cmd_v1 {
} __packed; /* STA_CMD_API_S_VER_1 */
/**
- * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's
+ * struct iwl_sta_cfg_cmd_v2 - cmd structure to add a peer sta to the uCode's
* station table
* ( STA_CONFIG_CMD = 0xA )
*
@@ -769,7 +837,7 @@ struct iwl_sta_cfg_cmd_v1 {
* @mic_compute_pad_delay: MIC compute time padding
* @reserved: Reserved for alignment
*/
-struct iwl_sta_cfg_cmd {
+struct iwl_sta_cfg_cmd_v2 {
__le32 sta_id;
__le32 link_id;
u8 peer_mld_address[ETH_ALEN];
@@ -800,6 +868,83 @@ struct iwl_sta_cfg_cmd {
} __packed; /* STA_CMD_API_S_VER_2 */
/**
+ * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's
+ * station table
+ * ( STA_CONFIG_CMD = 0xA )
+ *
+ * @sta_id: index of station in uCode's station table
+ * @link_mask: bitmap of link FW IDs used with this STA
+ * @peer_mld_address: the peers mld address
+ * @reserved_for_peer_mld_address: reserved
+ * @peer_link_address: the address of the link that is used to communicate
+ * with this sta
+ * @reserved_for_peer_link_address: reserved
+ * @station_type: type of this station. See &enum iwl_fw_sta_type
+ * @assoc_id: for GO only
+ * @beamform_flags: beam forming controls
+ * @mfp: indicates whether the STA uses management frame protection or not.
+ * @mimo: indicates whether the sta uses mimo or not
+ * @mimo_protection: indicates whether the sta uses mimo protection or not
+ * @ack_enabled: indicates that the AP supports receiving ACK-
+ * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG
+ * @trig_rnd_alloc: indicates that trigger based random allocation
+ * is enabled according to UORA element existence
+ * @tx_ampdu_spacing: minimum A-MPDU spacing:
+ * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density
+ * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K,
+ * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K.
+ * @sp_length: the size of the SP in actual number of frames
+ * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver
+ * enabled ACs.
+ * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY
+ * capa
+ * @htc_flags: which features are supported in HTC
+ * @use_ldpc_x2_cw: Indicates whether to use LDPC with double CW
+ * @use_icf: Indicates whether to use ICF instead of RTS
+ * @dps_pad_time: DPS (Dynamic Power Save) padding delay resolution to ensure
+ * proper timing alignment
+ * @dps_trans_delay: DPS minimal time that takes the peer to return to low power
+ * @dps_enabled: flag indicating whether or not DPS is enabled
+ * @mic_prep_pad_delay: MIC prep time padding
+ * @mic_compute_pad_delay: MIC compute time padding
+ * @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to
+ * @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address
+ * @reserved: Reserved for alignment
+ */
+struct iwl_sta_cfg_cmd {
+ __le32 sta_id;
+ __le32 link_mask;
+ u8 peer_mld_address[ETH_ALEN];
+ __le16 reserved_for_peer_mld_address;
+ u8 peer_link_address[ETH_ALEN];
+ __le16 reserved_for_peer_link_address;
+ __le32 station_type;
+ __le32 assoc_id;
+ __le32 beamform_flags;
+ __le32 mfp;
+ __le32 mimo;
+ __le32 mimo_protection;
+ __le32 ack_enabled;
+ __le32 trig_rnd_alloc;
+ __le32 tx_ampdu_spacing;
+ __le32 tx_ampdu_max_size;
+ __le32 sp_length;
+ __le32 uapsd_acs;
+ struct iwl_he_pkt_ext_v2 pkt_ext;
+ __le32 htc_flags;
+ u8 use_ldpc_x2_cw;
+ u8 use_icf;
+ u8 dps_pad_time;
+ u8 dps_trans_delay;
+ u8 dps_enabled;
+ u8 mic_prep_pad_delay;
+ u8 mic_compute_pad_delay;
+ u8 nmi_sta_id;
+ u8 ndi_local_addr[ETH_ALEN];
+ u8 reserved[2];
+} __packed; /* STA_CMD_API_S_VER_3 */
+
+/**
* struct iwl_aux_sta_cmd - command for AUX STA configuration
* ( AUX_STA_CMD = 0xB )
*
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
index 2a174c00b712..439a4530ec9f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
@@ -57,8 +57,7 @@ enum iwl_mac_protection_flags {
* @FW_MAC_TYPE_P2P_DEVICE: P2P Device
* @FW_MAC_TYPE_P2P_STA: P2P client
* @FW_MAC_TYPE_GO: P2P GO
- * @FW_MAC_TYPE_TEST: ?
- * @FW_MAC_TYPE_MAX: highest support MAC type
+ * @FW_MAC_TYPE_NAN: NAN (since version 4)
*/
enum iwl_mac_types {
FW_MAC_TYPE_FIRST = 1,
@@ -70,8 +69,7 @@ enum iwl_mac_types {
FW_MAC_TYPE_P2P_DEVICE,
FW_MAC_TYPE_P2P_STA,
FW_MAC_TYPE_GO,
- FW_MAC_TYPE_TEST,
- FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+ FW_MAC_TYPE_NAN,
}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
index bd6bf931866f..443a9a416325 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -204,7 +204,7 @@ struct iwl_nvm_get_info_phy {
} __packed; /* REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */
#define IWL_NUM_CHANNELS_V1 51
-#define IWL_NUM_CHANNELS 110
+#define IWL_NUM_CHANNELS_V2 110
/**
* struct iwl_nvm_get_info_regulatory_v1 - regulatory information
@@ -227,7 +227,7 @@ struct iwl_nvm_get_info_regulatory_v1 {
struct iwl_nvm_get_info_regulatory {
__le32 lar_enabled;
__le32 n_channels;
- __le32 channel_profile[IWL_NUM_CHANNELS];
+ __le32 channel_profile[IWL_NUM_CHANNELS_V2];
} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_2 */
/**
@@ -701,13 +701,23 @@ struct iwl_pnvm_init_complete_ntfy {
#define UATS_TABLE_COL_SIZE 13
/**
- * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD
+ * struct iwl_mcc_allowed_ap_type_cmd_v1 - struct for MCC_ALLOWED_AP_TYPE_CMD
* @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS)
* @reserved: reserved
*/
-struct iwl_mcc_allowed_ap_type_cmd {
+struct iwl_mcc_allowed_ap_type_cmd_v1 {
u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
__le16 reserved;
} __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_1 */
+/**
+ * struct iwl_mcc_allowed_ap_type_cmd - struct for MCC_ALLOWED_AP_TYPE_CMD
+ * @mcc_to_ap_type_map: mapping an MCC to 6 GHz AP type support (UATS)
+ * @mcc_to_ap_type_unii9_map: mapping an MCC to UNII-9 AP type support allowed
+ */
+struct iwl_mcc_allowed_ap_type_cmd {
+ u8 mcc_to_ap_type_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
+ u8 mcc_to_ap_type_unii9_map[UATS_TABLE_ROW_SIZE][UATS_TABLE_COL_SIZE];
+} __packed; /* MCC_ALLOWED_AP_TYPE_CMD_API_S_VER_2 */
+
#endif /* __iwl_fw_api_nvm_reg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
index 0cd8a12e0f7c..a3f916630df2 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
@@ -269,6 +269,7 @@ enum iwl_dev_tx_power_cmd_mode {
#define IWL_NUM_CHAIN_LIMITS 2
#define IWL_NUM_SUB_BANDS_V1 5
#define IWL_NUM_SUB_BANDS_V2 11
+#define IWL_NUM_SUB_BANDS_V3 12
/**
* struct iwl_dev_tx_power_common - Common part of the TX power reduction cmd
@@ -425,24 +426,38 @@ struct iwl_dev_tx_power_cmd_v10 {
__le32 flags;
} __packed; /* TX_REDUCED_POWER_API_S_VER_10 */
+struct iwl_dev_tx_power_cmd_v11 {
+ __le16 per_chain[IWL_NUM_CHAIN_TABLES_V2][IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3];
+ u8 per_chain_restriction_changed;
+ u8 reserved;
+ __le32 timer_period;
+ __le32 flags;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_11 */
+
/*
* struct iwl_dev_tx_power_cmd - TX power reduction command (multiversion)
* @common: common part of the command
* @v9: version 9 part of the command
* @v10: version 10 part of the command
+ * @v11: version 11 part of the command
*/
struct iwl_dev_tx_power_cmd {
struct iwl_dev_tx_power_common common;
union {
struct iwl_dev_tx_power_cmd_v9 v9;
struct iwl_dev_tx_power_cmd_v10 v10;
+ struct iwl_dev_tx_power_cmd_v11 v11;
};
-} __packed; /* TX_REDUCED_POWER_API_S_VER_9_VER10 */
+} __packed; /* TX_REDUCED_POWER_API_S_VER_9
+ * TX_REDUCED_POWER_API_S_VER_10
+ * TX_REDUCED_POWER_API_S_VER_11
+ */
#define IWL_NUM_GEO_PROFILES 3
#define IWL_NUM_GEO_PROFILES_V3 8
#define IWL_NUM_BANDS_PER_CHAIN_V1 2
#define IWL_NUM_BANDS_PER_CHAIN_V2 3
+#define IWL_NUM_BANDS_PER_CHAIN_V6 4
/**
* enum iwl_geo_per_chain_offset_operation - type of operation
@@ -524,12 +539,25 @@ struct iwl_geo_tx_power_profiles_cmd_v5 {
__le32 table_revision;
} __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_5 */
+/**
+ * struct iwl_geo_tx_power_profiles_cmd_v6 - struct for PER_CHAIN_LIMIT_OFFSET_CMD cmd.
+ * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation
+ * @table: offset profile per band.
+ * @bios_hdr: describes the revision and the source of the BIOS
+ */
+struct iwl_geo_tx_power_profiles_cmd_v6 {
+ __le32 ops;
+ struct iwl_per_chain_offset table[IWL_NUM_GEO_PROFILES_V3][IWL_NUM_BANDS_PER_CHAIN_V6];
+ struct iwl_bios_config_hdr bios_hdr;
+} __packed; /* PER_CHAIN_LIMIT_OFFSET_CMD_VER_6 */
+
union iwl_geo_tx_power_profiles_cmd {
struct iwl_geo_tx_power_profiles_cmd_v1 v1;
struct iwl_geo_tx_power_profiles_cmd_v2 v2;
struct iwl_geo_tx_power_profiles_cmd_v3 v3;
struct iwl_geo_tx_power_profiles_cmd_v4 v4;
struct iwl_geo_tx_power_profiles_cmd_v5 v5;
+ struct iwl_geo_tx_power_profiles_cmd_v6 v6;
};
/**
@@ -573,6 +601,7 @@ enum iwl_ppag_flags {
* @v1: command version 1 structure.
* @v5: command version 5 structure.
* @v7: command version 7 structure.
+ * @v8: command version 8 structure.
* @v1.flags: values from &enum iwl_ppag_flags
* @v1.gain: table of antenna gain values per chain and sub-band
* @v1.reserved: reserved
@@ -581,6 +610,8 @@ enum iwl_ppag_flags {
* @v7.ppag_config_info: see @struct bios_value_u32
* @v7.gain: table of antenna gain values per chain and sub-band
* @v7.reserved: reserved
+ * @v8.ppag_config_info: see @struct bios_value_u32
+ * @v8.gain: table of antenna gain values per chain and sub-band
*/
union iwl_ppag_table_cmd {
struct {
@@ -598,6 +629,10 @@ union iwl_ppag_table_cmd {
s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2];
s8 reserved[2];
} __packed v7; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */
+ struct {
+ struct bios_value_u32 ppag_config_info;
+ s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V3];
+ } __packed v8; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_8 */
} __packed;
#define IWL_PPAG_CMD_V1_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 60f0a4924ddf..c2bb400c834c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -985,6 +985,7 @@ struct iwl_scan_probe_params_v4 {
} __packed; /* SCAN_PROBE_PARAMS_API_S_VER_4 */
#define SCAN_MAX_NUM_CHANS_V3 67
+#define SCAN_MAX_NUM_CHANS_V4 68
/**
* struct iwl_scan_channel_params_v4 - channel params
@@ -1028,6 +1029,24 @@ struct iwl_scan_channel_params_v7 {
} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_6 */
/**
+ * struct iwl_scan_channel_params_v8 - channel params
+ * @flags: channel flags &enum iwl_scan_channel_flags
+ * @count: num of channels in scan request
+ * @n_aps_override: override the number of APs the FW uses to calculate dwell
+ * time when adaptive dwell is used.
+ * Channel k will use n_aps_override[i] when BIT(20 + i) is set in
+ * channel_config[k].flags
+ * @channel_config: array of explicit channel configurations
+ * for 2.4Ghz and 5.2Ghz bands
+ */
+struct iwl_scan_channel_params_v8 {
+ u8 flags;
+ u8 count;
+ u8 n_aps_override[2];
+ struct iwl_scan_channel_cfg_umac channel_config[SCAN_MAX_NUM_CHANS_V4];
+} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_8 */
+
+/**
* struct iwl_scan_general_params_v11 - channel params
* @flags: &enum iwl_umac_scan_general_flags_v2
* @reserved: reserved for future
@@ -1110,6 +1129,20 @@ struct iwl_scan_req_params_v17 {
} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_17 - 14 */
/**
+ * struct iwl_scan_req_params_v18 - scan request parameters (v18)
+ * @general_params: &struct iwl_scan_general_params_v11
+ * @channel_params: &struct iwl_scan_channel_params_v8
+ * @periodic_params: &struct iwl_scan_periodic_parms_v1
+ * @probe_params: &struct iwl_scan_probe_params_v4
+ */
+struct iwl_scan_req_params_v18 {
+ struct iwl_scan_general_params_v11 general_params;
+ struct iwl_scan_channel_params_v8 channel_params;
+ struct iwl_scan_periodic_parms_v1 periodic_params;
+ struct iwl_scan_probe_params_v4 probe_params;
+} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_18 */
+
+/**
* struct iwl_scan_req_umac_v12 - scan request command (v12)
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @ooc_priority: out of channel priority - &enum iwl_scan_priority
@@ -1134,6 +1167,18 @@ struct iwl_scan_req_umac_v17 {
} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_17 - 14 */
/**
+ * struct iwl_scan_req_umac_v18 - scan request command (v18)
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @scan_params: scan parameters
+ */
+struct iwl_scan_req_umac_v18 {
+ __le32 uid;
+ __le32 ooc_priority;
+ struct iwl_scan_req_params_v18 scan_params;
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_18 */
+
+/**
* struct iwl_umac_scan_abort - scan abort command
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @flags: reserved
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
index 8d9a5058d5a5..68983f6a0026 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
@@ -598,7 +598,6 @@ struct iwl_stats_ntfy_per_sta {
} __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */
#define IWL_STATS_MAX_PHY_OPERATIONAL 3
-#define IWL_STATS_MAX_FW_LINKS (IWL_FW_MAX_LINK_ID + 1)
/**
* struct iwl_system_statistics_notif_oper - statistics notification
@@ -610,7 +609,7 @@ struct iwl_stats_ntfy_per_sta {
*/
struct iwl_system_statistics_notif_oper {
__le32 time_stamp;
- struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+ struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
@@ -624,7 +623,7 @@ struct iwl_system_statistics_notif_oper {
*/
struct iwl_system_statistics_part1_notif_oper {
__le32 time_stamp;
- struct iwl_stats_ntfy_part1_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+ struct iwl_stats_ntfy_part1_per_link per_link[IWL_FW_MAX_LINKS];
__le32 per_phy_crc_error_stats[IWL_STATS_MAX_PHY_OPERATIONAL];
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index 1f26d89fc908..0cffa5493704 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -2933,7 +2933,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n",
le32_to_cpu(desc->trig_desc.type));
- queue_delayed_work(system_unbound_wq, &wk_data->wk,
+ queue_delayed_work(system_dfl_wq, &wk_data->wk,
usecs_to_jiffies(delay));
return 0;
@@ -3236,7 +3236,7 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
if (sync)
iwl_fw_dbg_collect_sync(fwrt, idx);
else
- queue_delayed_work(system_unbound_wq,
+ queue_delayed_work(system_dfl_wq,
&fwrt->dump.wks[idx].wk,
usecs_to_jiffies(delay));
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
index ddd714cff2f4..c2af66899a78 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -128,19 +128,11 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)
IWL_ERR(fwrt, "0x%08X | %s\n", table.error_id,
iwl_fw_lookup_assert_desc(table.error_id));
- IWL_ERR(fwrt, "0x%08X | umac branchlink1\n", table.blink1);
- IWL_ERR(fwrt, "0x%08X | umac branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | umac interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | umac interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | umac data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | umac data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | umac data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | umac major\n", table.umac_major);
- IWL_ERR(fwrt, "0x%08X | umac minor\n", table.umac_minor);
- IWL_ERR(fwrt, "0x%08X | frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | stack pointer\n", table.stack_pointer);
IWL_ERR(fwrt, "0x%08X | last host cmd\n", table.cmd_header);
- IWL_ERR(fwrt, "0x%08X | isr status reg\n", table.nic_isr_pref);
}
static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_num)
@@ -200,39 +192,10 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu
IWL_ERR(fwrt, "0x%08X | %-28s\n", table.error_id,
iwl_fw_lookup_assert_desc(table.error_id));
- IWL_ERR(fwrt, "0x%08X | trm_hw_status0\n", table.trm_hw_status0);
- IWL_ERR(fwrt, "0x%08X | trm_hw_status1\n", table.trm_hw_status1);
- IWL_ERR(fwrt, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(fwrt, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(fwrt, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(fwrt, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(fwrt, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(fwrt, "0x%08X | uCode revision type\n", table.fw_rev_type);
- IWL_ERR(fwrt, "0x%08X | uCode version major\n", table.major);
- IWL_ERR(fwrt, "0x%08X | uCode version minor\n", table.minor);
- IWL_ERR(fwrt, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(fwrt, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(fwrt, "0x%08X | hcmd\n", table.hcmd);
- IWL_ERR(fwrt, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(fwrt, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(fwrt, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(fwrt, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(fwrt, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(fwrt, "0x%08X | last cmd Id\n", table.last_cmd_id);
- IWL_ERR(fwrt, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(fwrt, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(fwrt, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(fwrt, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(fwrt, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(fwrt, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(fwrt, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(fwrt, "0x%08X | flow_handler\n", table.flow_handler);
}
/*
@@ -264,7 +227,6 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
struct iwl_trans *trans = fwrt->trans;
struct iwl_tcm_error_event_table table = {};
u32 base = fwrt->trans->dbg.tcm_error_event_table[idx];
- int i;
u32 flag = idx ? IWL_ERROR_EVENT_TABLE_TCM2 :
IWL_ERROR_EVENT_TABLE_TCM1;
@@ -275,23 +237,10 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
IWL_ERR(fwrt, "TCM%d status:\n", idx + 1);
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
- IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | tcm interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | tcm interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | tcm data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | tcm data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | tcm data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | tcm log PC\n", table.logpc);
- IWL_ERR(fwrt, "0x%08X | tcm frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | tcm stack pointer\n", table.stack_pointer);
- IWL_ERR(fwrt, "0x%08X | tcm msg ID\n", table.msgid);
- IWL_ERR(fwrt, "0x%08X | tcm ISR status\n", table.isr);
- for (i = 0; i < ARRAY_SIZE(table.hw_status); i++)
- IWL_ERR(fwrt, "0x%08X | tcm HW status[%d]\n",
- table.hw_status[i], i);
- for (i = 0; i < ARRAY_SIZE(table.sw_status); i++)
- IWL_ERR(fwrt, "0x%08X | tcm SW status[%d]\n",
- table.sw_status[i], i);
}
/*
@@ -338,26 +287,10 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
IWL_ERR(fwrt, "RCM%d status:\n", idx + 1);
IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
- IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2);
- IWL_ERR(fwrt, "0x%08X | rcm interruptlink1\n", table.ilink1);
IWL_ERR(fwrt, "0x%08X | rcm interruptlink2\n", table.ilink2);
IWL_ERR(fwrt, "0x%08X | rcm data1\n", table.data1);
IWL_ERR(fwrt, "0x%08X | rcm data2\n", table.data2);
IWL_ERR(fwrt, "0x%08X | rcm data3\n", table.data3);
- IWL_ERR(fwrt, "0x%08X | rcm log PC\n", table.logpc);
- IWL_ERR(fwrt, "0x%08X | rcm frame pointer\n", table.frame_pointer);
- IWL_ERR(fwrt, "0x%08X | rcm stack pointer\n", table.stack_pointer);
- IWL_ERR(fwrt, "0x%08X | rcm msg ID\n", table.msgid);
- IWL_ERR(fwrt, "0x%08X | rcm ISR status\n", table.isr);
- IWL_ERR(fwrt, "0x%08X | frame HW status\n", table.frame_hw_status);
- IWL_ERR(fwrt, "0x%08X | LMAC-to-RCM request mbox\n",
- table.mbx_lmac_to_rcm_req);
- IWL_ERR(fwrt, "0x%08X | RCM-to-LMAC request mbox\n",
- table.mbx_rcm_to_lmac_req);
- IWL_ERR(fwrt, "0x%08X | MAC header control\n", table.mh_ctl);
- IWL_ERR(fwrt, "0x%08X | MAC header addr1 low\n", table.mh_addr1_lo);
- IWL_ERR(fwrt, "0x%08X | MAC header info\n", table.mh_info);
- IWL_ERR(fwrt, "0x%08X | MAC header error\n", table.mh_err);
}
static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 378788de1d74..f7a6f21267e9 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -103,6 +103,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67,
IWL_UCODE_TLV_CURRENT_PC = 68,
IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72,
+ IWL_UCODE_TLV_CMD_BIOS_TABLE = 73,
/* contains sub-sections like PNVM file does (did) */
IWL_UCODE_TLV_PNVM_DATA = 74,
@@ -1040,6 +1041,20 @@ struct iwl_fw_cmd_version {
u8 notif_ver;
} __packed;
+/**
+ * struct iwl_fw_cmd_bios_table - firmware command BIOS revision entry
+ * @cmd: command ID
+ * @group: group ID
+ * @max_acpi_revision: max supported ACPI revision of command.
+ * @max_uefi_revision: max supported UEFI revision of command.
+ */
+struct iwl_fw_cmd_bios_table {
+ u8 cmd;
+ u8 group;
+ u8 max_acpi_revision;
+ u8 max_uefi_revision;
+} __packed;
+
struct iwl_fw_tcm_error_addr {
__le32 addr;
}; /* FW_TLV_TCM_ERROR_INFO_ADDRS_S */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.c b/drivers/net/wireless/intel/iwlwifi/fw/img.c
index c2f4fc83a22c..3cc1e3ae0858 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.c
@@ -1,11 +1,41 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2019 - 2021 Intel Corporation
- * Copyright(c) 2024 Intel Corporation
+ * Copyright(c) 2024 - 2025 Intel Corporation
*/
#include <fw/api/commands.h>
#include "img.h"
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def)
+{
+ const struct iwl_fw_cmd_bios_table *entry;
+ /* prior to LONG_GROUP, we never used this CMD version API */
+ u8 grp = iwl_cmd_groupid(cmd_id) ?: LONG_GROUP;
+ u8 cmd = iwl_cmd_opcode(cmd_id);
+
+ if (table_source != BIOS_SOURCE_ACPI &&
+ table_source != BIOS_SOURCE_UEFI)
+ return def;
+
+ if (!fw->ucode_capa.cmd_bios_tables ||
+ !fw->ucode_capa.n_cmd_bios_tables)
+ return def;
+
+ entry = fw->ucode_capa.cmd_bios_tables;
+ for (int i = 0; i < fw->ucode_capa.n_cmd_bios_tables; i++, entry++) {
+ if (entry->group == grp && entry->cmd == cmd) {
+ if (table_source == BIOS_SOURCE_ACPI)
+ return entry->max_acpi_revision;
+ return entry->max_uefi_revision;
+ }
+ }
+
+ return def;
+}
+EXPORT_SYMBOL_GPL(iwl_fw_lookup_cmd_bios_supported_revision);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def)
{
const struct iwl_fw_cmd_version *entry;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h
index 045a3e009429..94113d1db8e1 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include "api/dbg-tlv.h"
+#include "api/nvm-reg.h"
#include "file.h"
#include "error-dump.h"
@@ -57,6 +58,9 @@ struct iwl_ucode_capabilities {
const struct iwl_fw_cmd_version *cmd_versions;
u32 n_cmd_versions;
+
+ const struct iwl_fw_cmd_bios_table *cmd_bios_tables;
+ u32 n_cmd_bios_tables;
};
static inline bool
@@ -274,6 +278,10 @@ iwl_get_ucode_image(const struct iwl_fw *fw, enum iwl_ucode_type ucode_type)
return &fw->img[ucode_type];
}
+u8 iwl_fw_lookup_cmd_bios_supported_revision(const struct iwl_fw *fw,
+ enum bios_source table_source,
+ u32 cmd_id, u8 def);
+
u8 iwl_fw_lookup_cmd_ver(const struct iwl_fw *fw, u32 cmd_id, u8 def);
u8 iwl_fw_lookup_notif_ver(const struct iwl_fw *fw, u8 grp, u8 cmd, u8 def);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 958e71a3c958..55128caac7ed 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -241,6 +241,10 @@ static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b };
int i, j;
+ if (WARN_ON_ONCE(n_subbands >
+ ARRAY_SIZE(fwrt->sar_profiles[0].chains[0].subbands)))
+ return -EINVAL;
+
for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) {
struct iwl_sar_profile *prof;
@@ -300,132 +304,6 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt,
}
IWL_EXPORT_SYMBOL(iwl_sar_fill_profile);
-static bool iwl_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
- int subband)
-{
- s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband];
-
- if ((subband == 0 &&
- (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) ||
- (subband != 0 &&
- (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) {
- IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val);
- return false;
- }
- return true;
-}
-
-/* Utility function for iwlmvm and iwlxvt */
-int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
- union iwl_ppag_table_cmd *cmd, int *cmd_size)
-{
- u8 cmd_ver;
- int i, j, num_sub_bands;
- s8 *gain;
- bool send_ppag_always;
-
- /* many firmware images for JF lie about this */
- if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
- CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
- return -EOPNOTSUPP;
-
- if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
- IWL_DEBUG_RADIO(fwrt,
- "PPAG capability not supported by FW, command not sent.\n");
- return -EINVAL;
- }
-
- cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
- WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD), 1);
- /*
- * Starting from ver 4, driver needs to send the PPAG CMD regardless
- * if PPAG is enabled/disabled or valid/invalid.
- */
- send_ppag_always = cmd_ver > 3;
-
- /* Don't send PPAG if it is disabled */
- if (!send_ppag_always && !fwrt->ppag_flags) {
- IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
- return -EINVAL;
- }
-
- IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
- if (cmd_ver == 1) {
- num_sub_bands = IWL_NUM_SUB_BANDS_V1;
- gain = cmd->v1.gain[0];
- *cmd_size = sizeof(cmd->v1);
- cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK);
- if (fwrt->ppag_bios_rev >= 1) {
- /* in this case FW supports revision 0 */
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table rev is %d, send truncated table\n",
- fwrt->ppag_bios_rev);
- }
- } else if (cmd_ver == 5) {
- num_sub_bands = IWL_NUM_SUB_BANDS_V2;
- gain = cmd->v5.gain[0];
- *cmd_size = sizeof(cmd->v5);
- cmd->v5.flags = cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK);
- if (fwrt->ppag_bios_rev == 0) {
- /* in this case FW supports revisions 1,2 or 3 */
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table rev is 0, send padded table\n");
- }
- } else if (cmd_ver == 7) {
- num_sub_bands = IWL_NUM_SUB_BANDS_V2;
- gain = cmd->v7.gain[0];
- *cmd_size = sizeof(cmd->v7);
- cmd->v7.ppag_config_info.hdr.table_source =
- fwrt->ppag_bios_source;
- cmd->v7.ppag_config_info.hdr.table_revision =
- fwrt->ppag_bios_rev;
- cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
- } else {
- IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
- return -EINVAL;
- }
-
- /* ppag mode */
- IWL_DEBUG_RADIO(fwrt,
- "PPAG MODE bits were read from bios: %d\n",
- fwrt->ppag_flags);
-
- if (cmd_ver == 1 &&
- !fw_has_capa(&fwrt->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
- cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
- IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
- } else {
- IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
- }
-
- /* The 'flags' field is the same in v1 and v5 so we can just
- * use v1 to access it.
- */
- IWL_DEBUG_RADIO(fwrt,
- "PPAG MODE bits going to be sent: %d\n",
- (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) :
- le32_to_cpu(cmd->v7.ppag_config_info.value));
-
- for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
- for (j = 0; j < num_sub_bands; j++) {
- if (!send_ppag_always &&
- !iwl_ppag_value_valid(fwrt, i, j))
- return -EINVAL;
-
- gain[i * num_sub_bands + j] =
- fwrt->ppag_chains[i].subbands[j];
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table: chain[%d] band[%d]: gain = %d\n",
- i, j, gain[i * num_sub_bands + j]);
- }
- }
-
- return 0;
-}
-IWL_EXPORT_SYMBOL(iwl_fill_ppag_table);
-
bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt)
{
if (!dmi_check_system(dmi_ppag_approved_list)) {
@@ -440,6 +318,27 @@ bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt)
}
IWL_EXPORT_SYMBOL(iwl_is_ppag_approved);
+/* Print the PPAG table as read from BIOS */
+void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands)
+{
+ int i, j;
+
+ IWL_DEBUG_RADIO(fwrt, "PPAG table as read from BIOS:\n");
+ IWL_DEBUG_RADIO(fwrt, "PPAG revision = %d\n", fwrt->ppag_bios_rev);
+ IWL_DEBUG_RADIO(fwrt, "PPAG flags = 0x%x\n", fwrt->ppag_flags);
+
+ if (WARN_ON_ONCE(n_subbands >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands)))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(fwrt->ppag_chains); i++)
+ for (j = 0; j < n_subbands; j++)
+ IWL_DEBUG_RADIO(fwrt,
+ "ppag_chains[%d].subbands[%d] = %d\n",
+ i, j,
+ fwrt->ppag_chains[i].subbands[j]);
+}
+
bool iwl_is_tas_approved(void)
{
return dmi_check_system(dmi_tas_approved_list);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
index 1489031687b7..6fffc032efd3 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h
@@ -21,10 +21,11 @@
*/
#define BIOS_SAR_MAX_CHAINS_PER_PROFILE 4
#define BIOS_SAR_NUM_CHAINS 2
-#define BIOS_SAR_MAX_SUB_BANDS_NUM 11
+#define BIOS_SAR_MAX_SUB_BANDS_NUM 12
+#define BIOS_PPAG_MAX_SUB_BANDS_NUM 12
#define BIOS_GEO_NUM_CHAINS 2
-#define BIOS_GEO_MAX_NUM_BANDS 3
+#define BIOS_GEO_MAX_NUM_BANDS 4
#define BIOS_GEO_MAX_PROFILE_NUM 8
#define BIOS_GEO_MIN_PROFILE_NUM 3
@@ -100,7 +101,7 @@ struct iwl_geo_profile {
/* Same thing as with SAR, all revisions fit in revision 2 */
struct iwl_ppag_chain {
- s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM];
+ s8 subbands[BIOS_PPAG_MAX_SUB_BANDS_NUM];
};
struct iwl_tas_data {
@@ -180,6 +181,9 @@ enum iwl_dsm_masks_reg {
struct iwl_fw_runtime;
+/* Print the PPAG table as read from BIOS */
+void iwl_bios_print_ppag(struct iwl_fw_runtime *fwrt, int n_subbands);
+
bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt);
int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt,
@@ -190,10 +194,6 @@ int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt,
__le16 *per_chain, u32 n_tables, u32 n_subbands,
int prof_a, int prof_b);
-int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt,
- union iwl_ppag_table_cmd *cmd,
- int *cmd_size);
-
bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt);
bool iwl_is_tas_approved(void);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index ff186fb2e0da..d80ae610e56c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -106,13 +106,14 @@ struct iwl_txf_iter_data {
* @cur_fw_img: current firmware image, must be maintained by
* the driver by calling &iwl_fw_set_current_image()
* @dump: debug dump data
- * @uats_table: AP type table
- * @uats_valid: is AP type table valid
+ * @ap_type_cmd: AP type tables (for enablement on 6 GHz)
+ * @ap_type_cmd_valid: if &ap_type_cmd is valid
* @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock:
* 0: Unlocked, 1 and 2: Locked.
* Only read the UEFI variables if locked.
* @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables
* @geo_profiles: geographic profiles as read from WGDS BIOS table
+ * @geo_bios_source: see &enum bios_source
* @phy_filters: specific phy filters as read from WPFC BIOS table
* @ppag_bios_rev: PPAG BIOS revision
* @ppag_bios_source: see &enum bios_source
@@ -204,6 +205,7 @@ struct iwl_fw_runtime {
u8 sar_chain_b_profile;
u8 reduced_power_flags;
struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
+ enum bios_source geo_bios_source;
u32 geo_rev;
u32 geo_num_profiles;
bool geo_enabled;
@@ -213,8 +215,8 @@ struct iwl_fw_runtime {
u8 ppag_bios_source;
struct iwl_sar_offset_mapping_cmd sgom_table;
bool sgom_enabled;
- struct iwl_mcc_allowed_ap_type_cmd uats_table;
- bool uats_valid;
+ struct iwl_mcc_allowed_ap_type_cmd ap_type_cmd;
+ bool ap_type_cmd_valid;
u8 uefi_tables_lock_status;
struct iwl_phy_specific_cfg phy_filters;
enum bios_source dsm_source;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
index a7ba86e06c09..2ef0a7a920ad 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c
@@ -402,11 +402,11 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
if (uats_data->revision != 1)
return -EINVAL;
- memcpy(fwrt->uats_table.mcc_to_ap_type_map,
+ memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_map,
uats_data->mcc_to_ap_type_map,
- sizeof(fwrt->uats_table.mcc_to_ap_type_map));
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_map));
- fwrt->uats_valid = true;
+ fwrt->ap_type_cmd_valid = true;
return 0;
}
@@ -429,12 +429,61 @@ void iwl_uefi_get_uats_table(struct iwl_trans *trans,
}
IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
+void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt)
+{
+ struct uefi_cnv_wlan_uneb_data *data;
+
+ data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UNEB_NAME,
+ "UNEB", sizeof(*data), NULL);
+ if (IS_ERR(data))
+ return;
+
+ if (data->revision != 1) {
+ IWL_DEBUG_RADIO(fwrt,
+ "Cannot read UNEB table. rev is invalid\n");
+ goto out;
+ }
+
+ BUILD_BUG_ON(sizeof(data->mcc_to_ap_type_map) !=
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
+
+ memcpy(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map,
+ data->mcc_to_ap_type_map,
+ sizeof(fwrt->ap_type_cmd.mcc_to_ap_type_unii9_map));
+
+ fwrt->ap_type_cmd_valid = true;
+
+out:
+ kfree(data);
+}
+IWL_EXPORT_SYMBOL(iwl_uefi_get_uneb_table);
+
static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
- struct uefi_sar_profile *uefi_sar_prof,
- u8 prof_index, bool enabled)
+ const u8 *vals, u8 prof_index,
+ u8 num_subbands, bool enabled)
{
- memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof,
- sizeof(struct uefi_sar_profile));
+ struct iwl_sar_profile *sar_prof = &fwrt->sar_profiles[prof_index];
+
+ /*
+ * Make sure fwrt has enough room to hold the data
+ * coming from the UEFI table
+ */
+ if (WARN_ON(ARRAY_SIZE(sar_prof->chains) *
+ ARRAY_SIZE(sar_prof->chains[0].subbands) <
+ UEFI_SAR_MAX_CHAINS_PER_PROFILE * num_subbands))
+ return;
+
+ BUILD_BUG_ON(ARRAY_SIZE(sar_prof->chains) !=
+ UEFI_SAR_MAX_CHAINS_PER_PROFILE);
+
+ for (int chain = 0;
+ chain < UEFI_SAR_MAX_CHAINS_PER_PROFILE;
+ chain++) {
+ for (int subband = 0; subband < num_subbands; subband++)
+ sar_prof->chains[chain].subbands[subband] =
+ vals[chain * num_subbands + subband];
+ }
fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
}
@@ -442,24 +491,46 @@ static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_wrds *data;
+ unsigned long size;
+ unsigned long expected_size;
+ int num_subbands;
int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,
- "WRDS", sizeof(*data), NULL);
+ "WRDS",
+ UEFI_SAR_WRDS_TABLE_SIZE_REV2,
+ &size);
+
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_WRDS_REVISION) {
- ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n",
+ switch (data->revision) {
+ case 2:
+ expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV2;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+ break;
+ case 3:
+ expected_size = UEFI_SAR_WRDS_TABLE_SIZE_REV3;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+ break;
+ default:
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI WRDS revision:%d\n",
data->revision);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (size != expected_size) {
+ ret = -EINVAL;
goto out;
}
/* The profile from WRDS is officially profile 1, but goes
* into sar_profiles[0] (because we don't have a profile 0).
*/
- iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode);
+ iwl_uefi_set_sar_profile(fwrt, data->vals, 0,
+ num_subbands, data->mode);
out:
kfree(data);
return ret;
@@ -468,21 +539,40 @@ out:
int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_ewrd *data;
+ unsigned long expected_size;
int i, ret = 0;
+ unsigned long size;
+ int num_subbands;
+ int profile_size;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,
- "EWRD", sizeof(*data), NULL);
+ "EWRD",
+ UEFI_SAR_EWRD_TABLE_SIZE_REV2,
+ &size);
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_EWRD_REVISION) {
- ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n",
+ switch (data->revision) {
+ case 2:
+ expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV2;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV2;
+ profile_size = UEFI_SAR_PROFILE_SIZE_REV2;
+ break;
+ case 3:
+ expected_size = UEFI_SAR_EWRD_TABLE_SIZE_REV3;
+ num_subbands = UEFI_SAR_SUB_BANDS_NUM_REV3;
+ profile_size = UEFI_SAR_PROFILE_SIZE_REV3;
+ break;
+ default:
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI EWRD revision:%d\n",
data->revision);
+ ret = -EINVAL;
goto out;
}
- if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
+ if (size != expected_size ||
+ data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
ret = -EINVAL;
goto out;
}
@@ -492,8 +582,8 @@ int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
* save them in sar_profiles[1-3] (because we don't
* have profile 0). So in the array we start from 1.
*/
- iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1,
- data->mode);
+ iwl_uefi_set_sar_profile(fwrt, &data->vals[i * profile_size],
+ i + 1, num_subbands, data->mode);
out:
kfree(data);
@@ -503,20 +593,39 @@ out:
int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_wgds *data;
- int i, ret = 0;
+ unsigned long expected_size;
+ unsigned long size;
+ int profile_size;
+ int n_subbands;
+ int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,
- "WGDS", sizeof(*data), NULL);
+ "WGDS", UEFI_WGDS_TABLE_SIZE_REV3,
+ &size);
if (IS_ERR(data))
return -EINVAL;
- if (data->revision != IWL_UEFI_WGDS_REVISION) {
+ switch (data->revision) {
+ case 3:
+ expected_size = UEFI_WGDS_TABLE_SIZE_REV3;
+ n_subbands = UEFI_GEO_NUM_BANDS_REV3;
+ break;
+ case 4:
+ expected_size = UEFI_WGDS_TABLE_SIZE_REV4;
+ n_subbands = UEFI_GEO_NUM_BANDS_REV4;
+ break;
+ default:
ret = -EINVAL;
IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",
data->revision);
goto out;
}
+ if (size != expected_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||
data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {
ret = -EINVAL;
@@ -525,10 +634,31 @@ int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
goto out;
}
+ if (WARN_ON(BIOS_GEO_MAX_PROFILE_NUM >
+ ARRAY_SIZE(fwrt->geo_profiles) ||
+ n_subbands > ARRAY_SIZE(fwrt->geo_profiles[0].bands) ||
+ BIOS_GEO_NUM_CHAINS >
+ ARRAY_SIZE(fwrt->geo_profiles[0].bands[0].chains))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
fwrt->geo_rev = data->revision;
- for (i = 0; i < data->num_profiles; i++)
- memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i],
- sizeof(struct iwl_geo_profile));
+ fwrt->geo_bios_source = BIOS_SOURCE_UEFI;
+ profile_size = 3 * n_subbands;
+ for (int prof = 0; prof < data->num_profiles; prof++) {
+ const u8 *val = &data->vals[profile_size * prof];
+ struct iwl_geo_profile *geo_prof = &fwrt->geo_profiles[prof];
+
+ for (int subband = 0; subband < n_subbands; subband++) {
+ geo_prof->bands[subband].max = *val++;
+
+ for (int chain = 0;
+ chain < BIOS_GEO_NUM_CHAINS;
+ chain++)
+ geo_prof->bands[subband].chains[chain] = *val++;
+ }
+ }
fwrt->geo_num_profiles = data->num_profiles;
fwrt->geo_enabled = true;
@@ -540,28 +670,66 @@ out:
int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
{
struct uefi_cnv_var_ppag *data;
+ int n_subbands;
+ u32 valid_rev;
int ret = 0;
data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
- "PPAG", sizeof(*data), NULL);
- if (IS_ERR(data))
- return -EINVAL;
+ "PPAG", UEFI_PPAG_DATA_SIZE_V5,
+ NULL);
+ if (!IS_ERR(data)) {
+ n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV5;
+ valid_rev = BIT(5);
+
+ goto parse_table;
+ }
+
+ data = iwl_uefi_get_verified_variable(fwrt->trans,
+ IWL_UEFI_PPAG_NAME,
+ "PPAG",
+ UEFI_PPAG_DATA_SIZE_V4,
+ NULL);
+ if (!IS_ERR(data)) {
+ n_subbands = UEFI_PPAG_SUB_BANDS_NUM_REV4;
+ /* revisions 1-4 have all the same size */
+ valid_rev = BIT(1) | BIT(2) | BIT(3) | BIT(4);
+
+ goto parse_table;
+ }
- if (data->revision < IWL_UEFI_MIN_PPAG_REV ||
- data->revision > IWL_UEFI_MAX_PPAG_REV) {
+ return -EINVAL;
+
+parse_table:
+ if (!(BIT(data->revision) & valid_rev)) {
ret = -EINVAL;
- IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n",
+ IWL_DEBUG_RADIO(fwrt,
+ "Unsupported UEFI PPAG revision:%d\n",
data->revision);
goto out;
}
+ /*
+ * Make sure fwrt has enough room to hold
+ * data coming from the UEFI table
+ */
+ if (WARN_ON(ARRAY_SIZE(fwrt->ppag_chains) *
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands) <
+ UEFI_PPAG_NUM_CHAINS * n_subbands)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
fwrt->ppag_bios_rev = data->revision;
fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
fwrt->ppag_bios_rev);
- BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));
- memcpy(&fwrt->ppag_chains, &data->ppag_chains,
- sizeof(data->ppag_chains));
+ for (int chain = 0; chain < UEFI_PPAG_NUM_CHAINS; chain++) {
+ for (int subband = 0; subband < n_subbands; subband++)
+ fwrt->ppag_chains[chain].subbands[subband] =
+ data->vals[chain * n_subbands + subband];
+ }
+
+ iwl_bios_print_ppag(fwrt, n_subbands);
fwrt->ppag_bios_source = BIOS_SOURCE_UEFI;
out:
kfree(data);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
index 349ac1505ad7..474f06db4d43 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h
@@ -25,16 +25,12 @@
#define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing"
#define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR"
#define IWL_UEFI_WPFC_NAME L"WPFC"
+#define IWL_UEFI_UNEB_NAME L"CnvUefiWlanUNEB"
#define IWL_SGOM_MAP_SIZE 339
#define IWL_UATS_MAP_SIZE 339
-#define IWL_UEFI_WRDS_REVISION 2
-#define IWL_UEFI_EWRD_REVISION 2
-#define IWL_UEFI_WGDS_REVISION 3
-#define IWL_UEFI_MIN_PPAG_REV 1
-#define IWL_UEFI_MAX_PPAG_REV 4
#define IWL_UEFI_MIN_WTAS_REVISION 1
#define IWL_UEFI_MAX_WTAS_REVISION 2
#define IWL_UEFI_SPLC_REVISION 0
@@ -63,6 +59,9 @@ struct uefi_cnv_wlan_uats_data {
u8 mcc_to_ap_type_map[IWL_UATS_MAP_SIZE - 1];
} __packed;
+/* UNEB's layout is identical to UATS's */
+#define uefi_cnv_wlan_uneb_data uefi_cnv_wlan_uats_data
+
struct uefi_cnv_common_step_data {
u8 revision;
u8 step_mode;
@@ -72,68 +71,135 @@ struct uefi_cnv_common_step_data {
u8 radio2;
} __packed;
-/*
- * struct uefi_sar_profile - a SAR profile as defined in UEFI
- *
- * @chains: a per-chain table of SAR values
- */
-struct uefi_sar_profile {
- struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE];
-} __packed;
+#define UEFI_PPAG_SUB_BANDS_NUM_REV4 11
+#define UEFI_PPAG_SUB_BANDS_NUM_REV5 12
+#define UEFI_PPAG_NUM_CHAINS 2
-/*
+#define UEFI_SAR_SUB_BANDS_NUM_REV2 11
+#define UEFI_SAR_SUB_BANDS_NUM_REV3 12
+
+#define UEFI_SAR_MAX_CHAINS_PER_PROFILE 4
+
+#define UEFI_GEO_NUM_BANDS_REV3 3
+#define UEFI_GEO_NUM_BANDS_REV4 4
+
+/**
* struct uefi_cnv_var_wrds - WRDS table as defined in UEFI
*
* @revision: the revision of the table
* @mode: is WRDS enbaled/disabled
- * @sar_profile: sar profile #1
+ * @vals: values for sar profile #1 as an array:
+ * vals[chain * num_of_subbands + subband] will return the right value.
+ * num_of_subbands depends on the revision. For revision 3, it is
+ * %UEFI_SAR_SUB_BANDS_NUM_REV3, for earlier revision, it is
+ * %UEFI_SAR_SUB_BANDS_NUM_REV2.
+ * The max number of chains is currently 2
*/
struct uefi_cnv_var_wrds {
u8 revision;
u32 mode;
- struct uefi_sar_profile sar_profile;
+ u8 vals[];
} __packed;
-/*
+#define UEFI_SAR_PROFILE_SIZE_REV2 \
+ (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \
+ UEFI_SAR_SUB_BANDS_NUM_REV2)
+
+#define UEFI_SAR_PROFILE_SIZE_REV3 \
+ (sizeof(u8) * UEFI_SAR_MAX_CHAINS_PER_PROFILE * \
+ UEFI_SAR_SUB_BANDS_NUM_REV3)
+
+#define UEFI_SAR_WRDS_TABLE_SIZE_REV2 \
+ (offsetof(struct uefi_cnv_var_wrds, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV2)
+
+#define UEFI_SAR_WRDS_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_wrds, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV3)
+
+/**
* struct uefi_cnv_var_ewrd - EWRD table as defined in UEFI
* @revision: the revision of the table
* @mode: is WRDS enbaled/disabled
* @num_profiles: how many additional profiles we have in this table (0-3)
- * @sar_profiles: the additional SAR profiles (#2-#4)
+ * @vals: the additional SAR profiles (#2-#4) as an array of SAR profiles.
+ * A SAR profile is defined the &struct uefi_cnv_var_wrds::vals. The size
+ * of each profile depends on the number of subbands which depends on the
+ * revision. This is explained in &struct uefi_cnv_var_wrds.
*/
struct uefi_cnv_var_ewrd {
u8 revision;
u32 mode;
u32 num_profiles;
- struct uefi_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM - 1];
+ u8 vals[];
} __packed;
-/*
+#define UEFI_SAR_EWRD_TABLE_SIZE_REV2 \
+ (offsetof(struct uefi_cnv_var_ewrd, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV2 * (BIOS_SAR_MAX_PROFILE_NUM - 1))
+
+#define UEFI_SAR_EWRD_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_ewrd, vals) + \
+ UEFI_SAR_PROFILE_SIZE_REV3 * (BIOS_SAR_MAX_PROFILE_NUM - 1))
+
+/**
* struct uefi_cnv_var_wgds - WGDS table as defined in UEFI
* @revision: the revision of the table
* @num_profiles: the number of geo profiles we have in the table.
* The first 3 are mandatory, and can have up to 8.
- * @geo_profiles: a per-profile table of the offsets to add to SAR values.
+ * @vals: a per-profile table of the offsets to add to SAR values. This is an
+ * array of profiles, each profile is an array of
+ * &struct iwl_geo_profile_band, one for each subband.
+ * There are %UEFI_GEO_NUM_BANDS_REV3 or %UEFI_GEO_NUM_BANDS_REV4 subbands
+ * depending on the revision.
*/
struct uefi_cnv_var_wgds {
u8 revision;
u8 num_profiles;
- struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM];
+ u8 vals[];
} __packed;
-/*
+/* struct iwl_geo_profile_band is 3 bytes-long, but since it is not packed,
+ * we can't use sizeof()
+ */
+#define UEFI_WGDS_PROFILE_SIZE_REV3 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV3)
+
+#define UEFI_WGDS_PROFILE_SIZE_REV4 (sizeof(u8) * 3 * UEFI_GEO_NUM_BANDS_REV4)
+
+#define UEFI_WGDS_TABLE_SIZE_REV3 \
+ (offsetof(struct uefi_cnv_var_wgds, vals) + \
+ UEFI_WGDS_PROFILE_SIZE_REV3 * BIOS_GEO_MAX_PROFILE_NUM)
+
+#define UEFI_WGDS_TABLE_SIZE_REV4 \
+ (offsetof(struct uefi_cnv_var_wgds, vals) + \
+ UEFI_WGDS_PROFILE_SIZE_REV4 * BIOS_GEO_MAX_PROFILE_NUM)
+
+/**
* struct uefi_cnv_var_ppag - PPAG table as defined in UEFI
* @revision: the revision of the table
* @ppag_modes: values from &enum iwl_ppag_flags
- * @ppag_chains: the PPAG values per chain and band
+ * @vals: the PPAG values per chain and band as an array.
+ * vals[chain * num_of_subbands + subband] will return the right value.
+ * num_of_subbands depends on the revision. For revision 5, it is
+ * %UEFI_PPAG_SUB_BANDS_NUM_REV5, for earlier revision it is
+ * %UEFI_PPAG_SUB_BANDS_NUM_REV4.
+ * the max number of chains is currently 2
*/
struct uefi_cnv_var_ppag {
u8 revision;
u32 ppag_modes;
- struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS];
+ s8 vals[];
} __packed;
-/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
+#define UEFI_PPAG_DATA_SIZE_V4 \
+ (offsetof(struct uefi_cnv_var_ppag, vals) + \
+ sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV4)
+#define UEFI_PPAG_DATA_SIZE_V5 \
+ (offsetof(struct uefi_cnv_var_ppag, vals) + \
+ sizeof(s8) * UEFI_PPAG_NUM_CHAINS * UEFI_PPAG_SUB_BANDS_NUM_REV5)
+
+/**
+ * struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI
* @revision: the revision of the table
* @tas_selection: different options of TAS enablement.
* @black_list_size: the number of defined entried in the black list
@@ -146,7 +212,8 @@ struct uefi_cnv_var_wtas {
u16 black_list[IWL_WTAS_BLACK_LIST_MAX];
} __packed;
-/* struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI
+/**
+ * struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI
* @revision: the revision of the table
* @default_pwr_limit: The default maximum power per device
*/
@@ -155,7 +222,8 @@ struct uefi_cnv_var_splc {
u32 default_pwr_limit;
} __packed;
-/* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI
+/**
+ * struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI
* @revision: the revision of the table
* @mcc: country identifier as defined in ISO/IEC 3166-1 Alpha 2 code
*/
@@ -164,7 +232,8 @@ struct uefi_cnv_var_wrdd {
u32 mcc;
} __packed;
-/* struct uefi_cnv_var_eckv - ECKV table as defined in UEFI
+/**
+ * struct uefi_cnv_var_eckv - ECKV table as defined in UEFI
* @revision: the revision of the table
* @ext_clock_valid: indicates if external 32KHz clock is valid
*/
@@ -175,7 +244,8 @@ struct uefi_cnv_var_eckv {
#define UEFI_MAX_DSM_FUNCS 32
-/* struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI
+/**
+ * struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI
* @revision: the revision of the table
* @functions: payload of the different DSM functions
*/
@@ -185,7 +255,9 @@ struct uefi_cnv_var_general_cfg {
} __packed;
#define IWL_UEFI_WBEM_REV0_MASK (BIT(0) | BIT(1))
-/* struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined
+
+/**
+ * struct uefi_cnv_wlan_wbem_data - Bandwidth enablement per MCC as defined
* in UEFI
* @revision: the revision of the table
* @wbem_320mhz_per_mcc: enablement of 320MHz bandwidth per MCC
@@ -274,6 +346,8 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt);
void iwl_uefi_get_uats_table(struct iwl_trans *trans,
struct iwl_fw_runtime *fwrt);
+void iwl_uefi_get_uneb_table(struct iwl_trans *trans,
+ struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt);
int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value);
int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt);
@@ -373,6 +447,11 @@ iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
{
}
+static inline void
+iwl_uefi_get_uneb_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt)
+{
+}
+
static inline
int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 45cf2bc68e41..5f40cd15e27f 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -85,7 +85,6 @@ enum iwl_nvm_type {
#define IWL_WATCHDOG_DISABLED 0
#define IWL_DEF_WD_TIMEOUT 2500
#define IWL_LONG_WD_TIMEOUT 10000
-#define IWL_MAX_WD_TIMEOUT 120000
#define IWL_DEFAULT_MAX_TX_POWER 22
#define IWL_TX_CSUM_NETIF_FLAGS (NETIF_F_IPV6_CSUM | NETIF_F_IP_CSUM |\
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 475b3e417efa..d5ded4d3a30b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -133,6 +133,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
kfree(drv->fw.dbg.mem_tlv);
kfree(drv->fw.iml);
kfree(drv->fw.ucode_capa.cmd_versions);
+ kfree(drv->fw.ucode_capa.cmd_bios_tables);
kfree(drv->fw.phy_integration_ver);
kfree(drv->trans->dbg.pc_data);
drv->trans->dbg.pc_data = NULL;
@@ -1314,7 +1315,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (tlv_len != sizeof(u32))
goto invalid_tlv_len;
if (le32_to_cpup((const __le32 *)tlv_data) >
- IWL_FW_MAX_LINK_ID + 1) {
+ IWL_FW_MAX_LINKS) {
IWL_ERR(drv,
"%d is an invalid number of links\n",
le32_to_cpup((const __le32 *)tlv_data));
@@ -1426,6 +1427,26 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
return -ENOMEM;
drv->fw.pnvm_size = tlv_len;
break;
+ case IWL_UCODE_TLV_CMD_BIOS_TABLE:
+ if (tlv_len % sizeof(struct iwl_fw_cmd_bios_table)) {
+ IWL_ERR(drv,
+ "Invalid length for command bios table: %u\n",
+ tlv_len);
+ return -EINVAL;
+ }
+
+ if (capa->cmd_bios_tables) {
+ IWL_ERR(drv, "Duplicate TLV type 0x%02X detected\n",
+ tlv_type);
+ return -EINVAL;
+ }
+ capa->cmd_bios_tables = kmemdup(tlv_data, tlv_len,
+ GFP_KERNEL);
+ if (!capa->cmd_bios_tables)
+ return -ENOMEM;
+ capa->n_cmd_bios_tables =
+ tlv_len / sizeof(struct iwl_fw_cmd_bios_table);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 6d235c417fdd..8f3f651451bb 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -23,6 +23,8 @@
#include "fw/api/commands.h"
#include "fw/api/cmdhdr.h"
#include "fw/img.h"
+#include "fw/dbg.h"
+
#include "mei/iwl-mei.h"
/* NVM offsets (in words) definitions */
@@ -1702,6 +1704,11 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
band);
new_rule = false;
+ if (IWL_FW_CHECK(trans, !center_freq,
+ "Invalid channel %d (idx %d) in NVM\n",
+ nvm_chan[ch_idx], ch_idx))
+ continue;
+
if (!(ch_flags & NVM_CHANNEL_VALID)) {
iwl_nvm_print_channel_flags(dev, IWL_DL_LAR,
nvm_chan[ch_idx], ch_flags);
@@ -2031,7 +2038,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
if (empty_otp)
IWL_INFO(trans, "OTP is empty\n");
- nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS);
+ nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2);
if (!nvm) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index 89901786fd68..16b2c313e72b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -138,7 +138,7 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
IWL_RESET_MODE_FUNC_RESET,
IWL_RESET_MODE_PROD_RESET,
};
- static const enum iwl_reset_mode escalation_list_sc[] = {
+ static const enum iwl_reset_mode escalation_list_top[] = {
IWL_RESET_MODE_SW_RESET,
IWL_RESET_MODE_REPROBE,
IWL_RESET_MODE_REPROBE,
@@ -159,14 +159,14 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
if (trans->request_top_reset) {
trans->request_top_reset = 0;
- if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC)
+ if (iwl_trans_is_top_reset_supported(trans))
return IWL_RESET_MODE_TOP_RESET;
return IWL_RESET_MODE_PROD_RESET;
}
- if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
- escalation_list = escalation_list_sc;
- escalation_list_size = ARRAY_SIZE(escalation_list_sc);
+ if (iwl_trans_is_top_reset_supported(trans)) {
+ escalation_list = escalation_list_top;
+ escalation_list_size = ARRAY_SIZE(escalation_list_top);
} else {
escalation_list = escalation_list_old;
escalation_list_size = ARRAY_SIZE(escalation_list_old);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 688f9fee2821..61e4f4776dcb 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1088,7 +1088,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans,
*/
trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET,
&trans->status);
- queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0);
+ queue_delayed_work(system_dfl_wq, &trans->restart.wk, 0);
}
static inline void iwl_trans_fw_error(struct iwl_trans *trans,
@@ -1258,4 +1258,22 @@ bool iwl_trans_is_pm_supported(struct iwl_trans *trans);
bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans);
+static inline bool iwl_trans_is_top_reset_supported(struct iwl_trans *trans)
+{
+ /* not supported before Sc family */
+ if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ return false;
+
+ /* for Sc family only supported for Sc2/Sc2f */
+ if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_SC &&
+ CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_SC)
+ return false;
+
+ /* so far these numbers are increasing - not before Pe */
+ if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) < IWL_CFG_RF_TYPE_PE)
+ return false;
+
+ return true;
+}
+
#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
index 5d23a618ae3c..e2a5eecc18c3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
@@ -36,7 +36,6 @@
#define IWL_MLD_PS_HEAVY_RX_THLD_PACKETS 8
#define IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC 30
-#define IWL_MLD_SCAN_EXPIRE_TIME_SEC 20
#define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
index 19da521a4bab..7b1fb84a641c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
@@ -513,7 +513,7 @@ static int iwl_mld_config_fw(struct iwl_mld *mld)
return ret;
iwl_mld_init_tas(mld);
- iwl_mld_init_uats(mld);
+ iwl_mld_init_ap_type_tables(mld);
return 0;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 29df747c8938..46c8d943fd55 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -61,20 +61,27 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
struct iwl_mac_config_cmd *cmd)
{
+ u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD);
+ int len = sizeof(*cmd);
int ret;
lockdep_assert_wiphy(mld->wiphy);
- ret = iwl_mld_send_cmd_pdu(mld,
- WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
- cmd);
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) < 4) {
+ if (WARN_ON(cmd->mac_type == cpu_to_le32(FW_MAC_TYPE_NAN)))
+ return -EINVAL;
+
+ len = sizeof(struct iwl_mac_config_cmd_v3);
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len);
if (ret)
IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);
return ret;
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
+static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
{
switch (vif->type) {
case NL80211_IFTYPE_STATION:
@@ -386,7 +393,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
}
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
/* Constructor function for struct iwl_mld_vif */
static void
@@ -397,6 +404,7 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
lockdep_assert_wiphy(mld->wiphy);
mld_vif->mld = mld;
+ mld_vif->fw_id = IWL_MLD_INVALID_FW_ID;
mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
if (!mld->fw_status.in_hw_restart) {
@@ -444,6 +452,10 @@ void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
lockdep_assert_wiphy(mld->wiphy);
+ /* NAN interface type is not known to FW */
+ if (vif->type == NL80211_IFTYPE_NAN)
+ return;
+
iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 62fca166afd1..8dfc79fed253 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#ifndef __iwl_mld_iface_h__
#define __iwl_mld_iface_h__
@@ -33,6 +33,7 @@ enum iwl_mld_cca_40mhz_wa_status {
* there is an indication that a non-BSS interface is to be added.
* @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile
* @IWL_MLD_EMLSR_BLOCKED_NAN: NAN is preventing EMLSR.
+ * @IWL_MLD_EMLSR_BLOCKED_TDLS: TDLS connection is preventing EMLSR.
*/
enum iwl_mld_emlsr_blocked {
IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1,
@@ -42,6 +43,7 @@ enum iwl_mld_emlsr_blocked {
IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10,
IWL_MLD_EMLSR_BLOCKED_TPT = 0x20,
IWL_MLD_EMLSR_BLOCKED_NAN = 0x40,
+ IWL_MLD_EMLSR_BLOCKED_TDLS = 0x80,
};
/**
@@ -201,6 +203,15 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
}
+/* Call only for interfaces that were added to the driver! */
+static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
+{
+ if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif)))
+ return false;
+
+ return true;
+}
+
#define iwl_mld_link_dereference_check(mld_vif, link_id) \
rcu_dereference_check((mld_vif)->link[link_id], \
lockdep_is_held(&mld_vif->mld->wiphy->mtx))
@@ -219,8 +230,6 @@ iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id);
}
-int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif);
-
/* Cleanup function for struct iwl_mld_vif, will be called in restart */
void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif);
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index b5430e8a73d6..b66e84d2365f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -437,7 +437,7 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
}
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
/* Constructor function for struct iwl_mld_link */
static int
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 9e4da8e4de93..ca691259fc5e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -40,6 +40,7 @@ struct iwl_probe_resp_data {
* @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS.
* @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS.
* @mon_sta: station used for TX injection in monitor interface.
+ * @last_cqm_rssi_event: rssi of the last cqm rssi event
* @average_beacon_energy: average beacon energy for beacons received during
* client connections
* @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs,
@@ -66,6 +67,7 @@ struct iwl_mld_link {
struct iwl_mld_int_sta bcast_sta;
struct iwl_mld_int_sta mcast_sta;
struct iwl_mld_int_sta mon_sta;
+ int last_cqm_rssi_event;
/* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */
struct ieee80211_key_conf *ap_early_keys[6];
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
index d39dd36b08e3..a4ddc32e2860 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include "mld.h"
#include "iface.h"
@@ -77,9 +77,12 @@ static void iwl_mld_low_latency_iter(void *_data, u8 *mac,
bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC;
bool low_latency;
- if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result)))
+ if (!iwl_mld_vif_fw_id_valid(mld_vif))
return;
+ BUILD_BUG_ON(ARRAY_SIZE(mld->fw_id_to_vif) !=
+ ARRAY_SIZE(mld->low_latency.result));
+
low_latency = mld->low_latency.result[mld_vif->fw_id];
if (prev != low_latency)
@@ -272,8 +275,10 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,
if (WARN_ON_ONCE(!mld->low_latency.pkts_counters))
return;
- if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) ||
- queue >= mld->trans->info.num_rxqs))
+ if (!iwl_mld_vif_fw_id_valid(mld_vif))
+ return;
+
+ if (WARN_ON_ONCE(queue >= mld->trans->info.num_rxqs))
return;
if (mld->low_latency.stopped)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 0c53d6bd9651..e3aec814aa0d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -754,6 +754,30 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
mld->monitor.phy.valid = false;
}
+static
+int iwl_mld_mac80211_change_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type, bool p2p)
+{
+ enum nl80211_iftype old_type = vif->type;
+ bool old_p2p = vif->p2p;
+ int ret;
+
+ iwl_mld_mac80211_remove_interface(hw, vif);
+
+ /* set the new type for adding it cleanly */
+ vif->type = new_type;
+ vif->p2p = p2p;
+
+ ret = iwl_mld_mac80211_add_interface(hw, vif);
+
+ /* restore for mac80211, it will change it again */
+ vif->type = old_type;
+ vif->p2p = old_p2p;
+
+ return ret;
+}
+
struct iwl_mld_mc_iter_data {
struct iwl_mld *mld;
int port_id;
@@ -1124,6 +1148,8 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
/* Now activate the link */
if (iwl_mld_can_activate_link(mld, vif, link)) {
+ iwl_mld_tlc_update_phy(mld, vif, link);
+
ret = iwl_mld_activate_link(mld, link);
if (ret)
goto err;
@@ -1185,6 +1211,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
+ iwl_mld_tlc_update_phy(mld, vif, link);
+
/* in the non-MLO case, remove/re-add the link to clean up FW state.
* In MLO, it'll be done in drv_change_vif_link
*/
@@ -1727,14 +1755,23 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
return -EBUSY;
}
- ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER);
+ ret = iwl_mld_add_sta(mld, sta, vif);
if (ret)
return ret;
- /* just added first TDLS STA, so disable PM */
- if (sta->tdls && tdls_count == 0)
+ /* just added first TDLS STA, so disable PM and block EMLSR */
+ if (sta->tdls && tdls_count == 0) {
iwl_mld_update_mac_power(mld, vif, false);
+ /* TDLS requires single-link operation with
+ * direct peer communication.
+ * Block and exit EMLSR when TDLS is established.
+ */
+ iwl_mld_block_emlsr(mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TDLS,
+ iwl_mld_get_primary_link(vif));
+ }
+
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
mld_vif->ap_sta = sta;
@@ -1870,8 +1907,14 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
iwl_mld_remove_sta(mld, sta);
if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) {
- /* just removed last TDLS STA, so enable PM */
+ /* just removed last TDLS STA, so enable PM
+ * and unblock EMLSR
+ */
iwl_mld_update_mac_power(mld, vif, false);
+
+ /* Unblock EMLSR when TDLS connection is torn down */
+ iwl_mld_unblock_emlsr(mld, vif,
+ IWL_MLD_EMLSR_BLOCKED_TDLS);
}
} else {
return -EINVAL;
@@ -2716,6 +2759,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
.get_antenna = iwl_mld_get_antenna,
.set_antenna = iwl_mld_set_antenna,
.add_interface = iwl_mld_mac80211_add_interface,
+ .change_interface = iwl_mld_mac80211_change_interface,
.remove_interface = iwl_mld_mac80211_remove_interface,
.conf_tx = iwl_mld_mac80211_conf_tx,
.prepare_multicast = iwl_mld_mac80211_prepare_multicast,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index 66c7a7d31409..606cb64f8ea4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -205,7 +205,7 @@
struct iwl_mld {
/* Add here fields that need clean up on restart */
struct_group(zeroed_on_hw_restart,
- struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];
+ struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINKS];
struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
u8 used_phy_ids: NUM_PHY_CTX;
@@ -530,9 +530,9 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
#define IWL_MLD_INVALID_FW_ID 0xff
#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \
-static int \
+int \
iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
- u8 *fw_id, \
+ u8 *fw_id, \
struct ieee80211_##_mac80211_type *mac80211_ptr) \
{ \
u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index f842f5183223..9362e02d9e76 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -13,7 +13,8 @@
HOW(NON_BSS) \
HOW(TMP_NON_BSS) \
HOW(TPT) \
- HOW(NAN)
+ HOW(NAN) \
+ HOW(TDLS)
static const char *
iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked)
@@ -110,7 +111,6 @@ void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy,
}
#define IWL_MLD_TRIGGER_LINK_SEL_TIME (HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC)
-#define IWL_MLD_SCAN_EXPIRE_TIME (HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC)
/* Exit reasons that can cause longer EMLSR prevention */
#define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
index c9c83d1012f0..c04d77208971 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
@@ -2,7 +2,8 @@
/*
* Copyright (C) 2025 Intel Corporation
*/
-
+#ifndef __iwl_mld_nan_h__
+#define __iwl_mld_nan_h__
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
@@ -26,3 +27,5 @@ bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt,
u32 obj_id);
+
+#endif /* __iwl_mld_nan_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
index 0deaf179f07c..6887f9feaa5c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
@@ -32,9 +32,9 @@ struct iwl_mld_phy {
};
static inline struct iwl_mld_phy *
-iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel)
+iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *chanctx)
{
- return (void *)channel->drv_priv;
+ return (void *)chanctx->drv_priv;
}
/* Cleanup function for struct iwl_mld_phy, will be called in restart */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c
index c3318e84f4a2..49b0d9f8f865 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c
@@ -405,7 +405,10 @@ int iwl_mld_set_tx_power(struct iwl_mld *mld,
.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
.common.pwr_restriction = cpu_to_le16(u_tx_power),
};
- int len = sizeof(cmd.common) + sizeof(cmd.v10);
+ int len = sizeof(cmd.common) + sizeof(cmd.v11);
+
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 10) == 10)
+ len = sizeof(cmd.common) + sizeof(cmd.v10);
if (WARN_ON(!mld_link))
return -ENODEV;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
index 231920425c06..c65f4b56a327 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
@@ -301,10 +301,12 @@ void iwl_mld_ptp_init(struct iwl_mld *mld)
mld->ptp_data.ptp_clock =
ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
- if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
+ if (IS_ERR(mld->ptp_data.ptp_clock)) {
IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
PTR_ERR(mld->ptp_data.ptp_clock));
mld->ptp_data.ptp_clock = NULL;
+ } else if (!mld->ptp_data.ptp_clock) {
+ IWL_DEBUG_INFO(mld, "PTP module unavailable on this kernel\n");
} else {
IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
mld->ptp_data.ptp_clock_info.name,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index 6ab5a3410353..659243ada86c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -64,6 +64,7 @@ void iwl_mld_get_bios_tables(struct iwl_mld *mld)
}
iwl_uefi_get_uats_table(mld->trans, &mld->fwrt);
+ iwl_uefi_get_uneb_table(mld->trans, &mld->fwrt);
iwl_bios_get_phy_filters(&mld->fwrt);
}
@@ -72,16 +73,36 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
{
u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
/* Only set to South Korea if the table revision is 1 */
- __le32 sk = cpu_to_le32(mld->fwrt.geo_rev == 1 ? 1 : 0);
+ u8 sk = mld->fwrt.geo_rev == 1 ? 1 : 0;
union iwl_geo_tx_power_profiles_cmd cmd = {
.v5.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES),
- .v5.table_revision = sk,
};
+ u32 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
+ int n_subbands;
+ int cmd_size;
int ret;
- ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v5.table[0][0],
- ARRAY_SIZE(cmd.v5.table[0]),
- BIOS_GEO_MAX_PROFILE_NUM);
+ switch (cmd_ver) {
+ case 5:
+ n_subbands = ARRAY_SIZE(cmd.v5.table[0]);
+ cmd.v5.table_revision = cpu_to_le32(sk);
+ cmd_size = sizeof(cmd.v5);
+ break;
+ case 6:
+ n_subbands = ARRAY_SIZE(cmd.v6.table[0]);
+ cmd.v6.bios_hdr.table_revision = mld->fwrt.geo_rev;
+ cmd.v6.bios_hdr.table_source = mld->fwrt.geo_bios_source;
+ cmd_size = sizeof(cmd.v6);
+ break;
+ default:
+ WARN(false, "unsupported version: %d", cmd_ver);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(offsetof(typeof(cmd), v6.table) !=
+ offsetof(typeof(cmd), v5.table));
+ ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v6.table[0][0],
+ n_subbands, BIOS_GEO_MAX_PROFILE_NUM);
/* It is a valid scenario to not support SAR, or miss wgds table,
* but in that case there is no need to send the command.
@@ -89,28 +110,48 @@ static int iwl_mld_geo_sar_init(struct iwl_mld *mld)
if (ret)
return 0;
- return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, sizeof(cmd.v5));
+ return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size);
}
int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b)
{
- u32 cmd_id = REDUCE_TX_POWER_CMD;
struct iwl_dev_tx_power_cmd cmd = {
.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
- .v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags),
};
+ u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, REDUCE_TX_POWER_CMD, 10);
+ int num_subbands;
+ int cmd_size;
int ret;
+ switch (cmd_ver) {
+ case 10:
+ cmd.v10.flags = cpu_to_le32(mld->fwrt.reduced_power_flags);
+ cmd_size = sizeof(cmd.common) + sizeof(cmd.v10);
+ num_subbands = IWL_NUM_SUB_BANDS_V2;
+ break;
+ case 11:
+ cmd.v11.flags = cpu_to_le32(mld->fwrt.reduced_power_flags);
+ cmd_size = sizeof(cmd.common) + sizeof(cmd.v11);
+ num_subbands = IWL_NUM_SUB_BANDS_V3;
+ break;
+ default:
+ WARN_ONCE(1, "Bad version for REDUCE_TX_POWER_CMD: %d\n",
+ cmd_ver);
+ return -EOPNOTSUPP;
+ }
+
/* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */
- ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v10.per_chain[0][0][0],
- IWL_NUM_CHAIN_TABLES, IWL_NUM_SUB_BANDS_V2,
+ /* v10 and v11 have the same position for per_chain */
+ BUILD_BUG_ON(offsetof(typeof(cmd), v11.per_chain) !=
+ offsetof(typeof(cmd), v10.per_chain));
+ ret = iwl_sar_fill_profile(&mld->fwrt, &cmd.v11.per_chain[0][0][0],
+ IWL_NUM_CHAIN_TABLES, num_subbands,
prof_a, prof_b);
/* return on error or if the profile is disabled (positive number) */
if (ret)
return ret;
- return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd,
- sizeof(cmd.common) + sizeof(cmd.v10));
+ return iwl_mld_send_cmd_pdu(mld, REDUCE_TX_POWER_CMD, &cmd, cmd_size);
}
int iwl_mld_init_sar(struct iwl_mld *mld)
@@ -165,30 +206,86 @@ static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld)
{
struct iwl_fw_runtime *fwrt = &mld->fwrt;
union iwl_ppag_table_cmd cmd = {
- .v7.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source,
- .v7.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev,
- .v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
+ /* v7 and v8 have the same layout for the ppag_config_info */
+ .v8.ppag_config_info.hdr.table_source = fwrt->ppag_bios_source,
+ .v8.ppag_config_info.hdr.table_revision = fwrt->ppag_bios_rev,
+ .v8.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags),
};
+ u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD);
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 1);
+ int cmd_len = sizeof(cmd.v8);
+ u8 cmd_bios_rev;
int ret;
+ BUILD_BUG_ON(offsetof(typeof(cmd), v8.ppag_config_info.hdr) !=
+ offsetof(typeof(cmd), v7.ppag_config_info.hdr));
+ BUILD_BUG_ON(offsetof(typeof(cmd), v8.gain) !=
+ offsetof(typeof(cmd), v7.gain));
+
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain) > ARRAY_SIZE(fwrt->ppag_chains));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v7.gain[0]) >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain) > ARRAY_SIZE(fwrt->ppag_chains));
+ BUILD_BUG_ON(ARRAY_SIZE(cmd.v8.gain[0]) >
+ ARRAY_SIZE(fwrt->ppag_chains[0].subbands));
+
IWL_DEBUG_RADIO(fwrt,
"PPAG MODE bits going to be sent: %d\n",
fwrt->ppag_flags);
- for (int chain = 0; chain < IWL_NUM_CHAIN_LIMITS; chain++) {
- for (int subband = 0; subband < IWL_NUM_SUB_BANDS_V2; subband++) {
- cmd.v7.gain[chain][subband] =
- fwrt->ppag_chains[chain].subbands[subband];
- IWL_DEBUG_RADIO(fwrt,
- "PPAG table: chain[%d] band[%d]: gain = %d\n",
- chain, subband, cmd.v7.gain[chain][subband]);
+ /* Since ver 7 will be deprecated at some point, don't bother making
+ * this code generic for both ver 7 and ver 8: duplicate the code.
+ */
+ if (cmd_ver == 7) {
+ for (int chain = 0; chain < ARRAY_SIZE(cmd.v7.gain); chain++) {
+ for (int subband = 0;
+ subband < ARRAY_SIZE(cmd.v7.gain[0]);
+ subband++) {
+ cmd.v7.gain[chain][subband] =
+ fwrt->ppag_chains[chain].subbands[subband];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ chain, subband,
+ cmd.v7.gain[chain][subband]);
+ }
+ }
+ cmd_len = sizeof(cmd.v7);
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 4);
+ } else if (cmd_ver == 8) {
+ for (int chain = 0; chain < ARRAY_SIZE(cmd.v8.gain); chain++) {
+ for (int subband = 0;
+ subband < ARRAY_SIZE(cmd.v8.gain[0]);
+ subband++) {
+ cmd.v8.gain[chain][subband] =
+ fwrt->ppag_chains[chain].subbands[subband];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ chain, subband,
+ cmd.v8.gain[chain][subband]);
+ }
}
+ cmd_bios_rev =
+ iwl_fw_lookup_cmd_bios_supported_revision(fwrt->fw,
+ fwrt->ppag_bios_source,
+ cmd_id, 5);
+ } else {
+ WARN(1, "Bad version for PER_PLATFORM_ANT_GAIN_CMD %d\n",
+ cmd_ver);
+ return -EINVAL;
+ }
+
+ if (cmd_bios_rev < fwrt->ppag_bios_rev) {
+ IWL_ERR(mld,
+ "BIOS revision compatibility check failed - Supported: %d, Current: %d\n",
+ cmd_bios_rev, fwrt->ppag_bios_rev);
+ return 0;
}
IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
- ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP,
- PER_PLATFORM_ANT_GAIN_CMD),
- &cmd, sizeof(cmd.v7));
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_len);
if (ret < 0)
IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
ret);
@@ -352,21 +449,42 @@ void iwl_mld_configure_lari(struct iwl_mld *mld)
ret);
}
-void iwl_mld_init_uats(struct iwl_mld *mld)
+void iwl_mld_init_ap_type_tables(struct iwl_mld *mld)
{
int ret;
struct iwl_host_cmd cmd = {
.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
MCC_ALLOWED_AP_TYPE_CMD),
- .data[0] = &mld->fwrt.uats_table,
- .len[0] = sizeof(mld->fwrt.uats_table),
+ .data[0] = &mld->fwrt.ap_type_cmd,
+ .len[0] = sizeof(mld->fwrt.ap_type_cmd),
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
- if (!mld->fwrt.uats_valid)
+ if (!mld->fwrt.ap_type_cmd_valid)
return;
- ret = iwl_mld_send_cmd(mld, &cmd);
+ if (iwl_fw_lookup_cmd_ver(mld->fw, cmd.id, 1) == 1) {
+ struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd_v1 =
+ kzalloc(sizeof(*cmd_v1), GFP_KERNEL);
+
+ if (!cmd_v1)
+ return;
+
+ BUILD_BUG_ON(sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map) !=
+ sizeof(cmd_v1->mcc_to_ap_type_map));
+
+ memcpy(cmd_v1->mcc_to_ap_type_map,
+ mld->fwrt.ap_type_cmd.mcc_to_ap_type_map,
+ sizeof(mld->fwrt.ap_type_cmd.mcc_to_ap_type_map));
+
+ cmd.data[0] = cmd_v1;
+ cmd.len[0] = sizeof(*cmd_v1);
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ kfree(cmd_v1);
+ } else {
+ ret = iwl_mld_send_cmd(mld, &cmd);
+ }
+
if (ret)
IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
ret);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
index 3b01c645adda..5498c19789f4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h
@@ -9,7 +9,7 @@
void iwl_mld_get_bios_tables(struct iwl_mld *mld);
void iwl_mld_configure_lari(struct iwl_mld *mld);
-void iwl_mld_init_uats(struct iwl_mld *mld);
+void iwl_mld_init_ap_type_tables(struct iwl_mld *mld);
void iwl_mld_init_tas(struct iwl_mld *mld);
int iwl_mld_init_ppag(struct iwl_mld *mld);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 214dcfde2fb4..a2e586c6ea67 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include <net/mac80211.h>
@@ -791,6 +791,9 @@ static void *
iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
{
struct ieee80211_radiotap_tlv *tlv;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
tlv = skb_put(skb, sizeof(*tlv));
tlv->type = cpu_to_le16(type);
@@ -1234,8 +1237,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
-
switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
case 0:
if (he_type == RATE_MCS_HE_TYPE_TRIG) {
@@ -1329,7 +1330,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
struct sk_buff *skb)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(mld->monitor.cur_aid);
@@ -1353,8 +1353,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, &mld->monitor.cur_aid,
sizeof(mld->monitor.cur_aid));
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
#endif
@@ -1362,7 +1360,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
struct sk_buff *skb,
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(*ntfy);
@@ -1382,8 +1379,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, ntfy, vendor_data_len);
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
static void
@@ -1407,6 +1402,7 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
u32 rate_n_flags = phy_data->rate_n_flags;
u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ u32 he_type = u32_get_bits(rate_n_flags, RATE_MCS_HE_TYPE_MSK);
bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
/* bandwidth may be overridden to RU by PHY ntfy */
@@ -1481,6 +1477,12 @@ static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
rx_status->encoding = RX_ENC_EHT;
iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
break;
+ case RATE_MCS_MOD_TYPE_UHR:
+ rx_status->encoding = RX_ENC_UHR;
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ if (he_type == RATE_MCS_HE_TYPE_UHR_ELR)
+ rx_status->uhr.elr = 1;
+ break;
default:
WARN_ON_ONCE(1);
}
@@ -2204,8 +2206,9 @@ void iwl_mld_sync_rx_queues(struct iwl_mld *mld,
ret = wait_event_timeout(mld->rxq_sync.waitq,
READ_ONCE(mld->rxq_sync.state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n",
- mld->rxq_sync.state, mld->rxq_sync.cookie);
+ IWL_FW_CHECK(mld, !ret,
+ "RXQ sync failed: state=0x%lx, cookie=%d\n",
+ mld->rxq_sync.state, mld->rxq_sync.cookie);
out:
mld->rxq_sync.state = 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index a1a4cf3ab3d3..7ed107fb0e8d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -47,8 +47,6 @@
/* adaptive dwell number of APs override mask for social channels */
#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21)
-#define SCAN_TIMEOUT_MSEC (30000 * HZ)
-
/* minimal number of 2GHz and 5GHz channels in the regular scan request */
#define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4
@@ -116,6 +114,13 @@ struct iwl_mld_scan_params {
u8 bssid[ETH_ALEN] __aligned(2);
};
+struct iwl_scan_req_params_ptrs {
+ struct iwl_scan_general_params_v11 *general_params;
+ struct iwl_scan_channel_params_v8 *channel_params;
+ struct iwl_scan_periodic_parms_v1 *periodic_params;
+ struct iwl_scan_probe_params_v4 *probe_params;
+};
+
struct iwl_mld_scan_respect_p2p_go_iter_data {
struct ieee80211_vif *current_vif;
bool p2p_go;
@@ -512,9 +517,10 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
static void
iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld,
- struct iwl_scan_general_params_v11 *gp,
- struct iwl_mld_scan_params *params)
+ struct iwl_mld_scan_params *params,
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
+ struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params;
const struct iwl_mld_scan_timing_params *timing =
&scan_timing[params->type];
@@ -551,9 +557,10 @@ static void
iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_general_params_v11 *gp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
enum iwl_mld_scan_status scan_status)
{
+ struct iwl_scan_general_params_v11 *gp = scan_ptrs->general_params;
u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
scan_status);
u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif,
@@ -566,7 +573,7 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
gp->flags = cpu_to_le16(gen_flags);
gp->flags2 = gen_flags2;
- iwl_mld_scan_cmd_set_dwell(mld, gp, params);
+ iwl_mld_scan_cmd_set_dwell(mld, params, scan_ptrs);
if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1)
gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS;
@@ -577,9 +584,12 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
static int
iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params,
- struct iwl_scan_umac_schedule *schedule,
- __le16 *delay)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
+ struct iwl_scan_umac_schedule *schedule =
+ scan_ptrs->periodic_params->schedule;
+ __le16 *delay = &scan_ptrs->periodic_params->delay;
+
if (WARN_ON(!params->n_scan_plans ||
params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS))
return -EINVAL;
@@ -657,11 +667,12 @@ iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params,
static void
iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
- struct iwl_scan_probe_params_v4 *pp)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
int j, idex_s = 0, idex_b = 0;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
for (j = 0;
j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE;
@@ -725,13 +736,15 @@ iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params,
static void
iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params,
- struct iwl_scan_probe_params_v4 *pp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
u32 *bitmap_ssid)
{
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
+
pp->preq = params->preq;
if (params->scan_6ghz) {
- iwl_mld_scan_fill_6g_chan_list(params, pp);
+ iwl_mld_scan_fill_6g_chan_list(params, scan_ptrs);
return;
}
@@ -821,10 +834,12 @@ static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id)
static void
iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld,
struct ieee80211_channel **channels,
- struct iwl_scan_channel_params_v7 *cp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
int n_channels, u32 flags,
enum nl80211_iftype vif_type)
{
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
+
for (int i = 0; i < n_channels; i++) {
enum nl80211_band band = channels[i]->band;
struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i];
@@ -862,10 +877,11 @@ static u8
iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
u32 n_channels,
- struct iwl_scan_probe_params_v4 *pp,
- struct iwl_scan_channel_params_v7 *cp,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
enum nl80211_iftype vif_type)
{
+ struct iwl_scan_probe_params_v4 *pp = scan_ptrs->probe_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
struct cfg80211_scan_6ghz_params *scan_6ghz_params =
params->scan_6ghz_params;
u32 i;
@@ -1063,25 +1079,23 @@ static int
iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_req_params_v17 *scan_p)
+ struct iwl_scan_req_params_ptrs *scan_ptrs)
{
- struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params;
- struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
/* Explicitly clear the flags since most of them are not
* relevant for 6 GHz scan.
*/
- chan_p->flags = 0;
- chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params,
- params->n_channels,
- probe_p, chan_p,
- vif->type);
- if (!chan_p->count)
+ cp->flags = 0;
+ cp->count = iwl_mld_scan_cfg_channels_6g(mld, params,
+ params->n_channels,
+ scan_ptrs, vif->type);
+ if (!cp->count)
return -EINVAL;
if (!params->n_ssids ||
(params->n_ssids == 1 && !params->ssids[0].ssid_len))
- chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
+ cp->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER;
return 0;
}
@@ -1090,12 +1104,12 @@ static int
iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
struct iwl_mld_scan_params *params,
struct ieee80211_vif *vif,
- struct iwl_scan_req_params_v17 *scan_p,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
bool low_latency,
enum iwl_mld_scan_status scan_status,
u32 channel_cfg_flags)
{
- struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params;
+ struct iwl_scan_channel_params_v8 *cp = scan_ptrs->channel_params;
struct ieee80211_supported_band *sband =
&mld->nvm_data->bands[NL80211_BAND_6GHZ];
@@ -1107,14 +1121,14 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
if (params->scan_6ghz)
return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params,
- vif, scan_p);
+ vif, scan_ptrs);
/* relevant only for 2.4 GHz/5 GHz scan */
cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif,
low_latency);
cp->count = params->n_channels;
- iwl_mld_scan_cmd_set_channels(mld, params->channels, cp,
+ iwl_mld_scan_cmd_set_channels(mld, params->channels, scan_ptrs,
params->n_channels, channel_cfg_flags,
vif->type);
@@ -1144,49 +1158,148 @@ iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld,
return 0;
}
-static int
-iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
+struct iwl_scan_umac_handler {
+ u8 version;
+ int (*handler)(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct iwl_mld_scan_params *params,
enum iwl_mld_scan_status scan_status,
- bool low_latency)
+ int uid, u32 ooc_priority, bool low_latency);
+};
+
+#define IWL_SCAN_UMAC_HANDLER(_ver) { \
+ .version = _ver, \
+ .handler = iwl_mld_scan_umac_v##_ver, \
+}
+
+static int iwl_mld_scan_umac_common(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ struct iwl_scan_req_params_ptrs *scan_ptrs,
+ enum iwl_mld_scan_status scan_status,
+ bool low_latency)
{
- struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
- struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params;
u32 bitmap_ssid = 0;
- int uid, ret;
+ int ret;
- memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+ iwl_mld_scan_cmd_set_gen_params(mld, params, vif, scan_ptrs,
+ scan_status);
- /* find a free UID entry */
- uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
- if (uid < 0)
- return uid;
+ ret = iwl_mld_scan_cmd_set_sched_params(params, scan_ptrs);
+ if (ret)
+ return ret;
- cmd->uid = cpu_to_le32(uid);
- cmd->ooc_priority =
- cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status));
+ iwl_mld_scan_cmd_set_probe_params(params, scan_ptrs, &bitmap_ssid);
+
+ return iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_ptrs,
+ low_latency, scan_status,
+ bitmap_ssid);
+}
+
+static int iwl_mld_scan_umac_v18(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ int uid, u32 ooc_priority, bool low_latency)
+{
+ struct iwl_scan_req_umac_v18 *cmd = mld->scan.cmd;
+ struct iwl_scan_req_params_ptrs scan_ptrs = {
+ .general_params = &cmd->scan_params.general_params,
+ .probe_params = &cmd->scan_params.probe_params,
+ .channel_params = &cmd->scan_params.channel_params,
+ .periodic_params = &cmd->scan_params.periodic_params
+ };
+ int ret;
- iwl_mld_scan_cmd_set_gen_params(mld, params, vif,
- &scan_p->general_params, scan_status);
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V4))
+ return -EINVAL;
- ret = iwl_mld_scan_cmd_set_sched_params(params,
- scan_p->periodic_params.schedule,
- &scan_p->periodic_params.delay);
+ cmd->uid = cpu_to_le32(uid);
+ cmd->ooc_priority = cpu_to_le32(ooc_priority);
+
+ ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs,
+ scan_status, low_latency);
if (ret)
return ret;
- iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params,
- &bitmap_ssid);
+ return uid;
+}
+
+static int iwl_mld_scan_umac_v17(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ int uid, u32 ooc_priority, bool low_latency)
+{
+ struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd;
+ struct iwl_scan_req_params_ptrs scan_ptrs = {
+ .general_params = &cmd->scan_params.general_params,
+ .probe_params = &cmd->scan_params.probe_params,
+
+ /* struct iwl_scan_channel_params_v8 and struct
+ * iwl_scan_channel_params_v7 are almost identical. The only
+ * difference is that the newer version allows configuration of
+ * more channels. So casting here is ok as long as we ensure
+ * that we don't exceed the max number of channels supported by
+ * the older version (see the WARN_ON below).
+ */
+ .channel_params = (struct iwl_scan_channel_params_v8 *)
+ &cmd->scan_params.channel_params,
+ .periodic_params = &cmd->scan_params.periodic_params
+ };
+ int ret;
+
+ if (WARN_ON(params->n_channels > SCAN_MAX_NUM_CHANS_V3))
+ return -EINVAL;
+
+ cmd->uid = cpu_to_le32(uid);
+ cmd->ooc_priority = cpu_to_le32(ooc_priority);
- ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p,
- low_latency, scan_status,
- bitmap_ssid);
+ ret = iwl_mld_scan_umac_common(mld, vif, params, &scan_ptrs,
+ scan_status, low_latency);
if (ret)
return ret;
return uid;
}
+static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = {
+ /* set the newest version first to shorten the list traverse time */
+ IWL_SCAN_UMAC_HANDLER(18),
+ IWL_SCAN_UMAC_HANDLER(17),
+};
+
+static int
+iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct iwl_mld_scan_params *params,
+ enum iwl_mld_scan_status scan_status,
+ bool low_latency)
+{
+ int uid, err;
+ u32 ooc_priority;
+
+ memset(mld->scan.cmd, 0, mld->scan.cmd_size);
+ uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE);
+ if (uid < 0)
+ return uid;
+
+ ooc_priority = iwl_mld_scan_ooc_priority(scan_status);
+
+ for (size_t i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) {
+ const struct iwl_scan_umac_handler *ver_handler =
+ &iwl_scan_umac_handlers[i];
+
+ if (ver_handler->version != mld->scan.cmd_ver)
+ continue;
+
+ err = ver_handler->handler(mld, vif, params, scan_status,
+ uid, ooc_priority, low_latency);
+ return err ? : uid;
+ }
+
+ IWL_ERR(mld, "No handler for UMAC scan cmd version %d\n",
+ mld->scan.cmd_ver);
+
+ return -EINVAL;
+}
+
static bool
iwl_mld_scan_pass_all(struct iwl_mld *mld,
struct cfg80211_sched_scan_request *req)
@@ -1942,9 +2055,7 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf = NULL;
if (fw_link_id != IWL_MLD_INVALID_FW_ID)
- link_conf =
- wiphy_dereference(mld->wiphy,
- mld->fw_id_to_bss_conf[fw_link_id]);
+ link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
/* It is possible that by the time the scan is complete the
* link was already removed and is not valid.
@@ -2031,6 +2142,8 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
if (scan_cmd_ver == 17) {
scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17);
+ } else if (scan_cmd_ver == 18) {
+ scan_cmd_size = sizeof(struct iwl_scan_req_umac_v18);
} else {
IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver);
return -EINVAL;
@@ -2041,6 +2154,7 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
return -ENOMEM;
mld->scan.cmd_size = scan_cmd_size;
+ mld->scan.cmd_ver = scan_cmd_ver;
return 0;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
index 69110f0cfc8e..772b3a02c4c4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -109,6 +109,7 @@ enum iwl_mld_traffic_load {
* @traffic_load.status: The current traffic load status, see
* &enum iwl_mld_traffic_load
* @cmd_size: size of %cmd.
+ * @cmd_ver: version of the scan command format.
* @cmd: pointer to scan cmd buffer (allocated once in op mode start).
* @last_6ghz_passive_jiffies: stores the last 6GHz passive scan time
* in jiffies.
@@ -134,6 +135,7 @@ struct iwl_mld_scan {
/* And here fields that survive a fw restart */
size_t cmd_size;
void *cmd;
+ u8 cmd_ver;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
u64 last_mlo_scan_time;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index 6b7a89e050e6..4c97d12ce2d0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -398,12 +398,42 @@ static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta)
return htc_flags;
}
+/* Note: modifies the command depending on FW command version */
static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
- const struct iwl_sta_cfg_cmd *cmd)
+ struct iwl_sta_cfg_cmd *cmd)
{
- int ret = iwl_mld_send_cmd_pdu(mld,
- WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
- cmd);
+ int cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
+ int len = sizeof(*cmd);
+ int ret;
+
+ if (cmd_ver < 2) {
+ IWL_ERR(mld, "Unsupported STA_CONFIG_CMD version %d\n",
+ cmd_ver);
+ return -EINVAL;
+ } else if (cmd_ver == 2) {
+ struct iwl_sta_cfg_cmd_v2 *cmd_v2 = (void *)cmd;
+
+ if (WARN_ON(cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) ||
+ cmd->station_type == cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) ||
+ hweight32(le32_to_cpu(cmd->link_mask)) != 1))
+ return -EINVAL;
+ /*
+ * These fields are located in a different place in the struct of v2.
+ * The assumption is that UHR won't be used with FW that has v2.
+ */
+ if (WARN_ON(cmd->mic_prep_pad_delay || cmd->mic_compute_pad_delay))
+ return -EINVAL;
+
+ len = sizeof(struct iwl_sta_cfg_cmd_v2);
+ cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
+ } else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
+ cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
+ hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
+ return -EINVAL;
+ }
+
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len);
if (ret)
IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
return ret;
@@ -431,8 +461,8 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
return -EINVAL;
cmd.sta_id = cpu_to_le32(fw_id);
+ cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id));
cmd.station_type = cpu_to_le32(mld_sta->sta_type);
- cmd.link_id = cpu_to_le32(mld_link->fw_id);
memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN);
@@ -498,7 +528,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
return iwl_mld_send_sta_cmd(mld, &cmd);
}
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
static int
iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
@@ -725,14 +755,14 @@ iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type)
+ struct ieee80211_vif *vif)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
struct ieee80211_link_sta *link_sta;
int link_id;
int ret;
- ret = iwl_mld_init_sta(mld, sta, vif, type);
+ ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
if (ret)
return ret;
@@ -908,7 +938,7 @@ static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
goto unlock;
- for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
+ for (int i = 0; i < IWL_FW_MAX_LINKS; i++)
total_mpdus += tx ? queue_counter->per_link[i].tx :
queue_counter->per_link[i].rx;
@@ -982,7 +1012,7 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
- cmd.link_id = cpu_to_le32(fw_link_id);
+ cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
cmd.station_type = cpu_to_le32(internal_sta->sta_type);
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index 1897b121aae2..36288c2fb38c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -89,7 +89,7 @@ struct iwl_mld_per_link_mpdu_counter {
*/
struct iwl_mld_per_q_mpdu_counter {
spinlock_t lock;
- struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1];
+ struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINKS];
unsigned long window_start_time;
} ____cacheline_aligned_in_smp;
@@ -190,7 +190,7 @@ iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta)
}
int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta,
- struct ieee80211_vif *vif, enum iwl_fw_sta_type type);
+ struct ieee80211_vif *vif);
void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta);
int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
struct ieee80211_link_sta *link_sta);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 7b8709716324..54eb0ead78ee 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -369,15 +369,39 @@ out:
static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig,
struct ieee80211_bss_conf *bss_conf)
{
+ struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
int exit_emlsr_thresh;
+ int last_event;
if (sig == 0) {
IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n");
return;
}
- /* TODO: task=statistics handle CQM notifications */
+ if (WARN_ON(!link))
+ return;
+
+ /* CQM Notification */
+ if (vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) {
+ int thold = bss_conf->cqm_rssi_thold;
+ int hyst = bss_conf->cqm_rssi_hyst;
+
+ last_event = link->last_cqm_rssi_event;
+ if (thold && sig < thold &&
+ (last_event == 0 || sig < last_event - hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig, GFP_KERNEL);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ link->last_cqm_rssi_event = sig;
+ ieee80211_cqm_rssi_notify(vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig, GFP_KERNEL);
+ }
+ }
if (!iwl_mld_emlsr_active(vif)) {
/* We're not in EMLSR and our signal is bad,
@@ -407,14 +431,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
u32 total_airtime_usec = 0;
for (u32 fw_id = 0;
- fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf);
+ fw_id < mld->fw->ucode_capa.num_links;
fw_id++) {
const struct iwl_stats_ntfy_per_link *link_stats;
struct ieee80211_bss_conf *bss_conf;
int sig;
- bss_conf = wiphy_dereference(mld->wiphy,
- mld->fw_id_to_bss_conf[fw_id]);
+ bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id);
if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION)
continue;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
index 26cf27be762d..dce747270167 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
@@ -42,7 +42,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
iwl_construct_mld(mld, trans, cfg, fw, hw, NULL);
fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
- fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1;
+ fw->ucode_capa.num_links = IWL_FW_MAX_LINKS;
mld->fwrt.trans = trans;
mld->fwrt.fw = fw;
@@ -68,7 +68,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
return 0;
}
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link,
@@ -94,7 +94,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
rcu_assign_pointer(vif->link_conf[link_id], link);
}
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
/* Helper function to add and initialize a VIF for KUnit tests */
struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
@@ -199,7 +199,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
vif->active_links |= BIT(link->link_id);
}
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
struct ieee80211_link_sta *link_sta,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
index 62a54c37a98c..78d6162d9297 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -9,6 +9,7 @@
#include "hcmd.h"
#include "sta.h"
#include "phy.h"
+#include "iface.h"
#include "fw/api/rs.h"
#include "fw/api/context.h"
@@ -36,7 +37,8 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
const struct ieee80211_sta_he_cap *own_he_cap,
- const struct ieee80211_sta_eht_cap *own_eht_cap)
+ const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap)
{
struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
@@ -90,6 +92,12 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld,
flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK;
}
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap &&
+ link_sta->uhr_cap.phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX &&
+ own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX)
+ flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK |
+ IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK;
+
return cpu_to_le16(flags);
}
@@ -406,6 +414,7 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_supported_band *sband,
const struct ieee80211_sta_he_cap *own_he_cap,
const struct ieee80211_sta_eht_cap *own_eht_cap,
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap,
struct iwl_tlc_config_cmd *cmd)
{
int i;
@@ -423,7 +432,16 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif,
cmd->non_ht_rates = cpu_to_le16(non_ht_rates);
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
- if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
+ if (link_sta->uhr_cap.has_uhr && own_uhr_cap) {
+ cmd->mode = IWL_TLC_MNG_MODE_UHR;
+ /*
+ * FIXME: spec currently inherits from EHT but has no
+ * finer MCS bits. Once that's there, need to add them
+ * to the bitmaps (and maybe copy this to UHR, or so.)
+ */
+ iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
+ own_eht_cap, cmd);
+ } else if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) {
cmd->mode = IWL_TLC_MNG_MODE_EHT;
iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap,
own_eht_cap, cmd);
@@ -513,19 +531,23 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
struct ieee80211_bss_conf *link)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
enum nl80211_band band = link->chanreq.oper.chan->band;
struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
const struct ieee80211_sta_he_cap *own_he_cap =
ieee80211_get_he_iftype_cap_vif(sband, vif);
const struct ieee80211_sta_eht_cap *own_eht_cap =
ieee80211_get_eht_iftype_cap_vif(sband, vif);
+ const struct ieee80211_sta_uhr_cap *own_uhr_cap =
+ ieee80211_get_uhr_iftype_cap_vif(sband, vif);
struct iwl_tlc_config_cmd cmd = {
/* For AP mode, use 20 MHz until the STA is authorized */
.max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ?
iwl_mld_fw_bw_from_sta_bw(link_sta) :
IWL_TLC_MNG_CH_WIDTH_20MHZ,
.flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta,
- own_he_cap, own_eht_cap),
+ own_he_cap, own_eht_cap,
+ own_uhr_cap),
.chains = iwl_mld_get_fw_chains(mld),
.sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta),
.max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len),
@@ -546,7 +568,10 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
- chan_ctx = rcu_dereference_wiphy(mld->wiphy, link->chanctx_conf);
+ if (WARN_ON_ONCE(!mld_link))
+ return;
+
+ chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
if (WARN_ON(!chan_ctx))
return;
@@ -555,7 +580,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
iwl_mld_fill_supp_rates(mld, vif, link_sta, sband,
own_he_cap, own_eht_cap,
- &cmd);
+ own_uhr_cap, &cmd);
if (cmd_ver == 6) {
cmd_ptr = &cmd;
@@ -638,6 +663,49 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf);
}
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+ struct ieee80211_chanctx_conf *chan_ctx;
+ int link_id = link_conf->link_id;
+ struct ieee80211_sta *sta;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ if (WARN_ON(!mld_link))
+ return;
+
+ chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
+
+ for_each_station(sta, mld->hw) {
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_link_sta *mld_link_sta;
+ struct ieee80211_link_sta *link_sta;
+
+ if (mld_sta->vif != vif)
+ continue;
+
+ link_sta = link_sta_dereference_protected(sta, link_id);
+ if (!link_sta)
+ continue;
+
+ mld_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
+ link_id);
+
+ /* In recovery flow, the station may not be (yet) in the
+ * firmware, don't send a TLC command for a station the
+ * firmware does not know.
+ */
+ if (!mld_link_sta || !mld_link_sta->in_fw)
+ continue;
+
+ if (chan_ctx)
+ iwl_mld_config_tlc_link(mld, vif, link_conf, link_sta);
+ /* TODO: else, remove the TLC object in the firmware */
+ }
+}
+
void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
index c32f42e8840b..c7ff209c9ab6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
@@ -20,4 +20,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data);
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf);
+
#endif /* __iwl_mld_tlc_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 43cf94c9a36b..0c643f0b7105 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -459,23 +459,18 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
{
+ int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
+ MCC_ALLOWED_AP_TYPE_CMD);
+ struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {};
u8 cmd_ver;
int ret;
- struct iwl_host_cmd cmd = {
- .id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
- MCC_ALLOWED_AP_TYPE_CMD),
- .flags = 0,
- .data[0] = &mvm->fwrt.uats_table,
- .len[0] = sizeof(mvm->fwrt.uats_table),
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- };
if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}
- cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
+ cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
IWL_FW_CMD_VER_UNKNOWN);
if (cmd_ver != 1) {
IWL_DEBUG_RADIO(mvm,
@@ -486,10 +481,17 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
- if (!mvm->fwrt.uats_valid)
+ if (!mvm->fwrt.ap_type_cmd_valid)
return;
- ret = iwl_mvm_send_cmd(mvm, &cmd);
+ BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) !=
+ sizeof(cmd.mcc_to_ap_type_map));
+
+ memcpy(cmd.mcc_to_ap_type_map,
+ mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map,
+ sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map));
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
if (ret < 0)
IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
ret);
@@ -906,7 +908,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
{
- union iwl_geo_tx_power_profiles_cmd geo_tx_cmd;
+ union iwl_geo_tx_power_profiles_cmd geo_tx_cmd = {};
struct iwl_geo_tx_power_profiles_resp *resp;
u16 len;
int ret;
@@ -958,7 +960,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
{
u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
- union iwl_geo_tx_power_profiles_cmd cmd;
+ union iwl_geo_tx_power_profiles_cmd cmd = {};
u16 len;
u32 n_bands;
u32 n_profiles;
@@ -1032,12 +1034,139 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);
}
+static bool iwl_mvm_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain,
+ int subband)
+{
+ s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband];
+
+ if ((subband == 0 &&
+ (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) ||
+ (subband != 0 &&
+ (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) {
+ IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val);
+ return false;
+ }
+ return true;
+}
+
+static int iwl_mvm_fill_ppag_table(struct iwl_fw_runtime *fwrt,
+ union iwl_ppag_table_cmd *cmd,
+ int *cmd_size)
+{
+ u8 cmd_ver;
+ int i, j, num_sub_bands;
+ s8 *gain;
+ bool send_ppag_always;
+
+ /* many firmware images for JF lie about this */
+ if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) ==
+ CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
+ return -EOPNOTSUPP;
+
+ if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG capability not supported by FW, command not sent.\n");
+ return -EINVAL;
+ }
+
+ cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
+ WIDE_ID(PHY_OPS_GROUP,
+ PER_PLATFORM_ANT_GAIN_CMD), 1);
+ /*
+ * Starting from ver 4, driver needs to send the PPAG CMD regardless
+ * if PPAG is enabled/disabled or valid/invalid.
+ */
+ send_ppag_always = cmd_ver > 3;
+
+ /* Don't send PPAG if it is disabled */
+ if (!send_ppag_always && !fwrt->ppag_flags) {
+ IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
+ return -EINVAL;
+ }
+
+ IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
+ if (cmd_ver == 1) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V1;
+ gain = cmd->v1.gain[0];
+ *cmd_size = sizeof(cmd->v1);
+ cmd->v1.flags =
+ cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK);
+ if (fwrt->ppag_bios_rev >= 1) {
+ /* in this case FW supports revision 0 */
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table rev is %d, send truncated table\n",
+ fwrt->ppag_bios_rev);
+ }
+ } else if (cmd_ver == 5) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V2;
+ gain = cmd->v5.gain[0];
+ *cmd_size = sizeof(cmd->v5);
+ cmd->v5.flags =
+ cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK);
+ if (fwrt->ppag_bios_rev == 0) {
+ /* in this case FW supports revisions 1,2 or 3 */
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table rev is 0, send padded table\n");
+ }
+ } else if (cmd_ver == 7) {
+ num_sub_bands = IWL_NUM_SUB_BANDS_V2;
+ gain = cmd->v7.gain[0];
+ *cmd_size = sizeof(cmd->v7);
+ cmd->v7.ppag_config_info.hdr.table_source =
+ fwrt->ppag_bios_source;
+ cmd->v7.ppag_config_info.hdr.table_revision =
+ fwrt->ppag_bios_rev;
+ cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags);
+ } else {
+ IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
+ return -EINVAL;
+ }
+
+ /* ppag mode */
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG MODE bits were read from bios: %d\n",
+ fwrt->ppag_flags);
+
+ if (cmd_ver == 1 &&
+ !fw_has_capa(&fwrt->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) {
+ cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
+ IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
+ } else {
+ IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
+ }
+
+ /* The 'flags' field is the same in v1 and v5 so we can just
+ * use v1 to access it.
+ */
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG MODE bits going to be sent: %d\n",
+ (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) :
+ le32_to_cpu(cmd->v7.ppag_config_info.value));
+
+ for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
+ for (j = 0; j < num_sub_bands; j++) {
+ if (!send_ppag_always &&
+ !iwl_mvm_ppag_value_valid(fwrt, i, j))
+ return -EINVAL;
+
+ gain[i * num_sub_bands + j] =
+ fwrt->ppag_chains[i].subbands[j];
+ IWL_DEBUG_RADIO(fwrt,
+ "PPAG table: chain[%d] band[%d]: gain = %d\n",
+ i, j, gain[i * num_sub_bands + j]);
+ }
+ }
+
+ return 0;
+}
+
int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)
{
union iwl_ppag_table_cmd cmd;
int ret, cmd_size;
- ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size);
+ ret = iwl_mvm_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size);
/* Not supporting PPAG table is a valid scenario */
if (ret < 0)
return 0;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 090791fe0638..1ec9807e4827 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -6229,9 +6229,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
ret = wait_event_timeout(mvm->rx_sync_waitq,
READ_ONCE(mvm->queue_sync_state) == 0,
SYNC_RX_QUEUE_TIMEOUT);
- WARN_ONCE(!ret, "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
- mvm->queue_sync_state,
- mvm->queue_sync_cookie);
+ IWL_FW_CHECK(mvm, !ret,
+ "queue sync: failed to sync, state is 0x%lx, cookie %d\n",
+ mvm->queue_sync_state,
+ mvm->queue_sync_cookie);
}
out:
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
index 9bb253dcf4a7..4869a5fa8abc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c
@@ -121,52 +121,6 @@ struct iwl_mvm_sta_key_update_data {
int err;
};
-static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- void *_data)
-{
- u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
- struct iwl_mvm_sta_key_update_data *data = _data;
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_sec_key_cmd cmd = {
- .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
- .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
- .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
- .u.modify.key_id = cpu_to_le32(key->keyidx),
- .u.modify.key_flags =
- cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)),
- };
- int err;
-
- /* only need to do this for pairwise keys (link_id == -1) */
- if (sta != data->sta || key->link_id >= 0)
- return;
-
- err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
-
- if (err)
- data->err = err;
-}
-
-int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_mvm_sta_key_update_data data = {
- .sta = sta,
- .old_sta_mask = old_sta_mask,
- .new_sta_mask = new_sta_mask,
- };
-
- ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key,
- &data);
- return data.err;
-}
-
static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,
u32 key_flags, u32 keyidx, u32 flags)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
index bf54b90a7c51..b65825747b9d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
@@ -6,7 +6,7 @@
static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd,
+ struct iwl_mac_config_cmd_v3 *cmd,
int cmd_ver)
{
if (vif->type == NL80211_IFTYPE_AP) {
@@ -24,7 +24,7 @@ static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd,
+ struct iwl_mac_config_cmd_v3 *cmd,
u32 action)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -83,7 +83,7 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
}
static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
- struct iwl_mac_config_cmd *cmd)
+ struct iwl_mac_config_cmd_v3 *cmd)
{
int ret = iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
@@ -98,7 +98,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action, bool force_assoc_off)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_STATION);
@@ -151,7 +151,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
@@ -170,7 +170,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
@@ -187,7 +187,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
@@ -210,7 +210,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
u32 action)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_config_cmd cmd = {};
+ struct iwl_mac_config_cmd_v3 cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_AP);
@@ -286,7 +286,7 @@ int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_config_cmd cmd = {
+ struct iwl_mac_config_cmd_v3 cmd = {
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
.id_and_color = cpu_to_le32(mvmvif->id),
};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
index 896ed9823021..f1dbfeae20bc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
@@ -886,133 +886,6 @@ static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops);
}
-static int
-iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u16 old_links, u16 new_links,
- struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
-{
- struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {};
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u16 removed = old_links & ~new_links;
- u16 added = new_links & ~old_links;
- int err, i;
-
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- break;
-
- if (!(added & BIT(i)))
- continue;
- new_link[i] = kzalloc_obj(*new_link[i]);
- if (!new_link[i]) {
- err = -ENOMEM;
- goto free;
- }
-
- new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID;
- iwl_mvm_init_link(new_link[i]);
- }
-
- mutex_lock(&mvm->mutex);
-
- /* If we're in RESTART flow, the default link wasn't added in
- * drv_add_interface(), and link[0] doesn't point to it.
- */
- if (old_links == 0 && !test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status)) {
- err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf);
- if (err)
- goto out_err;
- mvmvif->link[0] = NULL;
- }
-
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- if (removed & BIT(i)) {
- struct ieee80211_bss_conf *link_conf = old[i];
-
- err = iwl_mvm_disable_link(mvm, vif, link_conf);
- if (err)
- goto out_err;
- kfree(mvmvif->link[i]);
- mvmvif->link[i] = NULL;
- } else if (added & BIT(i)) {
- struct ieee80211_bss_conf *link_conf;
-
- link_conf = link_conf_dereference_protected(vif, i);
- if (WARN_ON(!link_conf))
- continue;
-
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status))
- mvmvif->link[i] = new_link[i];
- new_link[i] = NULL;
- err = iwl_mvm_add_link(mvm, vif, link_conf);
- if (err)
- goto out_err;
- }
- }
-
- err = 0;
- if (new_links == 0) {
- mvmvif->link[0] = &mvmvif->deflink;
- err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);
- }
-
-out_err:
- /* we really don't have a good way to roll back here ... */
- mutex_unlock(&mvm->mutex);
-
-free:
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
- kfree(new_link[i]);
- return err;
-}
-
-static int
-iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-
- guard(mvm)(mvm);
- return iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links);
-}
-
-static bool iwl_mvm_mld_can_activate_links(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- u16 desired_links)
-{
- int n_links = hweight16(desired_links);
-
- if (n_links <= 1)
- return true;
-
- WARN_ON(1);
- return false;
-}
-
-static enum ieee80211_neg_ttlm_res
-iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_neg_ttlm *neg_ttlm)
-{
- u16 map;
- u8 i;
-
- /* Verify all TIDs are mapped to the same links set */
- map = neg_ttlm->downlink[0];
- for (i = 0; i < IEEE80211_TTLM_NUM_TIDS; i++) {
- if (neg_ttlm->downlink[i] != neg_ttlm->uplink[i] ||
- neg_ttlm->uplink[i] != map)
- return NEG_TTLM_RES_REJECT;
- }
-
- return NEG_TTLM_RES_ACCEPT;
-}
-
const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.tx = iwl_mvm_mac_tx,
.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
@@ -1102,9 +975,4 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs,
#endif
.set_hw_timestamp = iwl_mvm_set_hw_timestamp,
-
- .change_vif_links = iwl_mvm_mld_change_vif_links,
- .change_sta_links = iwl_mvm_mld_change_sta_links,
- .can_activate_links = iwl_mvm_mld_can_activate_links,
- .can_neg_ttlm = iwl_mvm_mld_can_neg_ttlm,
};
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
index 3359e02e151f..da7ed4639a93 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c
@@ -20,7 +20,7 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
}
static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
- struct iwl_sta_cfg_cmd *cmd)
+ struct iwl_sta_cfg_cmd_v2 *cmd)
{
u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD);
int cmd_len = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) > 1 ?
@@ -41,7 +41,7 @@ static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta,
const u8 *addr, int link_id)
{
- struct iwl_sta_cfg_cmd cmd;
+ struct iwl_sta_cfg_cmd_v2 cmd;
lockdep_assert_held(&mvm->mutex);
@@ -416,7 +416,7 @@ static int iwl_mvm_mld_cfg_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_vif_link_info *link_info =
mvm_vif->link[link_conf->link_id];
- struct iwl_sta_cfg_cmd cmd = {
+ struct iwl_sta_cfg_cmd_v2 cmd = {
.sta_id = cpu_to_le32(mvm_link_sta->sta_id),
.station_type = cpu_to_le32(mvm_sta->sta_type),
};
@@ -913,288 +913,3 @@ void iwl_mvm_mld_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
rcu_read_unlock();
}
-
-static int iwl_mvm_mld_update_sta_queues(struct iwl_mvm *mvm,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_scd_queue_cfg_cmd cmd = {
- .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY),
- .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask),
- .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask),
- };
- struct iwl_host_cmd hcmd = {
- .id = WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD),
- .len[0] = sizeof(cmd),
- .data[0] = &cmd
- };
- int tid;
- int ret;
-
- lockdep_assert_held(&mvm->mutex);
-
- for (tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) {
- struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[tid];
- int txq_id = tid_data->txq_id;
-
- if (txq_id == IWL_MVM_INVALID_QUEUE)
- continue;
-
- if (tid == IWL_MAX_TID_COUNT)
- cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID);
- else
- cmd.u.modify.tid = cpu_to_le32(tid);
-
- ret = iwl_mvm_send_cmd(mvm, &hcmd);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int iwl_mvm_mld_update_sta_baids(struct iwl_mvm *mvm,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- struct iwl_rx_baid_cfg_cmd cmd = {
- .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),
- .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),
- .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),
- };
- u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);
- int baid;
-
- /* mac80211 will remove sessions later, but we ignore all that */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- return 0;
-
- BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));
-
- for (baid = 0; baid < ARRAY_SIZE(mvm->baid_map); baid++) {
- struct iwl_mvm_baid_data *data;
- int ret;
-
- data = rcu_dereference_protected(mvm->baid_map[baid],
- lockdep_is_held(&mvm->mutex));
- if (!data)
- continue;
-
- if (!(data->sta_mask & old_sta_mask))
- continue;
-
- WARN_ONCE(data->sta_mask != old_sta_mask,
- "BAID data for %d corrupted - expected 0x%x found 0x%x\n",
- baid, old_sta_mask, data->sta_mask);
-
- cmd.modify.tid = cpu_to_le32(data->tid);
-
- ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_SEND_IN_RFKILL,
- sizeof(cmd), &cmd);
- data->sta_mask = new_sta_mask;
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int iwl_mvm_mld_update_sta_resources(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask)
-{
- int ret;
-
- ret = iwl_mvm_mld_update_sta_queues(mvm, sta,
- old_sta_mask,
- new_sta_mask);
- if (ret)
- return ret;
-
- ret = iwl_mvm_mld_update_sta_keys(mvm, vif, sta,
- old_sta_mask,
- new_sta_mask);
- if (ret)
- return ret;
-
- return iwl_mvm_mld_update_sta_baids(mvm, old_sta_mask, new_sta_mask);
-}
-
-int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links)
-{
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
- struct iwl_mvm_vif *mvm_vif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mvm_link_sta *mvm_sta_link;
- struct iwl_mvm_vif_link_info *mvm_vif_link;
- unsigned long links_to_add = ~old_links & new_links;
- unsigned long links_to_rem = old_links & ~new_links;
- unsigned long old_links_long = old_links;
- u32 current_sta_mask = 0, sta_mask_added = 0, sta_mask_to_rem = 0;
- unsigned long link_sta_added_to_fw = 0, link_sta_allocated = 0;
- unsigned int link_id;
- int ret;
-
- lockdep_assert_wiphy(mvm->hw->wiphy);
- lockdep_assert_held(&mvm->mutex);
-
- for_each_set_bit(link_id, &old_links_long,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- if (WARN_ON(!mvm_sta_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- current_sta_mask |= BIT(mvm_sta_link->sta_id);
- if (links_to_rem & BIT(link_id))
- sta_mask_to_rem |= BIT(mvm_sta_link->sta_id);
- }
-
- if (sta_mask_to_rem) {
- ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
- current_sta_mask,
- current_sta_mask &
- ~sta_mask_to_rem);
- if (WARN_ON(ret))
- goto err;
-
- current_sta_mask &= ~sta_mask_to_rem;
- }
-
- for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
- mvm_vif_link = mvm_vif->link[link_id];
-
- if (WARN_ON(!mvm_sta_link || !mvm_vif_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- ret = iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
- if (WARN_ON(ret))
- goto err;
-
- if (vif->type == NL80211_IFTYPE_STATION)
- mvm_vif_link->ap_sta_id = IWL_INVALID_STA;
-
- iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id);
- }
-
- for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
- struct ieee80211_bss_conf *link_conf =
- link_conf_dereference_protected(vif, link_id);
- struct ieee80211_link_sta *link_sta =
- link_sta_dereference_protected(sta, link_id);
- mvm_vif_link = mvm_vif->link[link_id];
-
- if (WARN_ON(!mvm_vif_link || !link_conf || !link_sta)) {
- ret = -EINVAL;
- goto err;
- }
-
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- struct iwl_mvm_link_sta *mvm_link_sta =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
- u32 sta_id;
-
- if (WARN_ON(!mvm_link_sta)) {
- ret = -EINVAL;
- goto err;
- }
-
- sta_id = mvm_link_sta->sta_id;
-
- rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
- rcu_assign_pointer(mvm->fw_id_to_link_sta[sta_id],
- link_sta);
- } else {
- if (WARN_ON(mvm_sta->link[link_id])) {
- ret = -EINVAL;
- goto err;
- }
- ret = iwl_mvm_mld_alloc_sta_link(mvm, vif, sta,
- link_id);
- if (WARN_ON(ret))
- goto err;
- }
-
- link_sta->agg.max_rc_amsdu_len = 1;
- ieee80211_sta_recalc_aggregates(sta);
-
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- if (WARN_ON(!mvm_sta_link)) {
- ret = -EINVAL;
- goto err;
- }
-
- if (vif->type == NL80211_IFTYPE_STATION)
- iwl_mvm_mld_set_ap_sta_id(sta, mvm_vif_link,
- mvm_sta_link);
-
- link_sta_allocated |= BIT(link_id);
-
- sta_mask_added |= BIT(mvm_sta_link->sta_id);
-
- ret = iwl_mvm_mld_cfg_sta(mvm, sta, vif, link_sta, link_conf,
- mvm_sta_link);
- if (WARN_ON(ret))
- goto err;
-
- link_sta_added_to_fw |= BIT(link_id);
-
- iwl_mvm_rs_add_sta_link(mvm, mvm_sta_link);
-
- iwl_mvm_rs_rate_init(mvm, vif, sta, link_conf, link_sta,
- link_conf->chanreq.oper.chan->band);
- }
-
- if (sta_mask_added) {
- ret = iwl_mvm_mld_update_sta_resources(mvm, vif, sta,
- current_sta_mask,
- current_sta_mask |
- sta_mask_added);
- if (WARN_ON(ret))
- goto err;
- }
-
- return 0;
-
-err:
- /* remove all already allocated stations in FW */
- for_each_set_bit(link_id, &link_sta_added_to_fw,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- iwl_mvm_mld_rm_sta_from_fw(mvm, mvm_sta_link->sta_id);
- }
-
- /* remove all already allocated station links in driver */
- for_each_set_bit(link_id, &link_sta_allocated,
- IEEE80211_MLD_MAX_NUM_LINKS) {
- mvm_sta_link =
- rcu_dereference_protected(mvm_sta->link[link_id],
- lockdep_is_held(&mvm->mutex));
-
- iwl_mvm_mld_free_sta_link(mvm, mvm_sta, mvm_sta_link, link_id);
- }
-
- return ret;
-}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 46a9dfa58a53..402ba5dee8b2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -2450,11 +2450,6 @@ void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_mvm_vif_link_info *link,
unsigned int link_id);
-int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u32 old_sta_mask,
- u32 new_sta_mask);
int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags,
struct ieee80211_key_conf *keyconf);
u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
index ad156b82eaa9..f7b620136c85 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
@@ -304,7 +304,9 @@ void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
PTR_ERR(mvm->ptp_data.ptp_clock));
mvm->ptp_data.ptp_clock = NULL;
- } else if (mvm->ptp_data.ptp_clock) {
+ } else if (!mvm->ptp_data.ptp_clock) {
+ IWL_DEBUG_INFO(mvm, "PTP module unavailable on this kernel\n");
+ } else {
IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
mvm->ptp_data.ptp_clock_info.name,
ptp_clock_index(mvm->ptp_data.ptp_clock));
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
index c25edc7c1813..ff099aec7886 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -637,10 +637,6 @@ void iwl_mvm_mld_free_sta_link(struct iwl_mvm *mvm,
struct iwl_mvm_link_sta *mvm_sta_link,
unsigned int link_id);
int iwl_mvm_mld_rm_sta_id(struct iwl_mvm *mvm, u8 sta_id);
-int iwl_mvm_mld_update_sta_links(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- u16 old_links, u16 new_links);
u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int filter_link_id);
int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
index 4945ebf19f6b..a7cd2e4ba1ae 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
@@ -234,7 +234,7 @@ void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
* Also convert TU to msec.
*/
delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
@@ -548,7 +548,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
*/
delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
vif->bss_conf.beacon_int);
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
return 0;
}
@@ -659,6 +659,6 @@ retry:
/* register a timeout in case we don't succeed in switching */
delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
1024 / 1000;
- mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ mod_delayed_work(system_percpu_wq, &mvm->tdls_cs.dwork,
msecs_to_jiffies(delay));
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
index b15c5d486527..a50e845cea42 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
@@ -95,7 +95,9 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
-void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+static void
+_iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans,
+ bool dump_on_timeout)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
@@ -133,7 +135,7 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
"timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
inta_hw, reset_done);
- if (!reset_done) {
+ if (!reset_done && dump_on_timeout) {
struct iwl_fw_error_dump_mode mode = {
.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
.context = IWL_ERR_CONTEXT_FROM_OPMODE,
@@ -147,6 +149,11 @@ void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
trans_pcie->fw_reset_state = FW_RESET_IDLE;
}
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+{
+ _iwl_trans_pcie_fw_reset_handshake(trans, false);
+}
+
static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -163,7 +170,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
* should assume that the firmware is already dead.
*/
trans->state = IWL_TRANS_NO_FW;
- iwl_trans_pcie_fw_reset_handshake(trans);
+ _iwl_trans_pcie_fw_reset_handshake(trans, true);
}
trans_pcie->is_down = true;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
index 4560d92d76fe..a05f60f9224b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
@@ -3197,7 +3197,7 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file,
if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
return -EINVAL;
if (mode == IWL_RESET_MODE_TOP_RESET) {
- if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)
+ if (!iwl_trans_is_top_reset_supported(trans))
return -EINVAL;
trans->request_top_reset = 1;
}
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 2b705bdb7993..05eee64706ea 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c
@@ -88,6 +88,9 @@ void mt76_change_chanctx(struct ieee80211_hw *hw,
IEEE80211_CHANCTX_CHANGE_RADAR)))
return;
+ if (phy->roc_vif)
+ mt76_abort_roc(phy);
+
cancel_delayed_work_sync(&phy->mac_work);
mutex_lock(&dev->mutex);
@@ -155,8 +158,6 @@ void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
{
struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv;
struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
- struct mt76_vif_data *mvif = mlink->mvif;
- int link_id = link_conf->link_id;
struct mt76_phy *phy = ctx->phy;
struct mt76_dev *dev = phy->dev;
@@ -173,15 +174,8 @@ void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw,
if (!mlink)
goto out;
- if (mlink != (struct mt76_vif_link *)vif->drv_priv)
- rcu_assign_pointer(mvif->link[link_id], NULL);
-
dev->drv->vif_link_remove(phy, vif, link_conf, mlink);
mlink->ctx = NULL;
-
- if (mlink != (struct mt76_vif_link *)vif->drv_priv)
- kfree_rcu(mlink, rcu_head);
-
out:
mutex_unlock(&dev->mutex);
}
@@ -254,6 +248,8 @@ skip_link_replace:
continue;
mlink->ctx = vifs->new_ctx;
+ if (mlink->beacon_mon_interval)
+ WRITE_ONCE(mlink->beacon_mon_last, jiffies);
}
out:
@@ -324,9 +320,11 @@ void mt76_roc_complete(struct mt76_phy *phy)
if (mlink)
mlink->mvif->roc_phy = NULL;
- if (phy->main_chandef.chan &&
- !test_bit(MT76_MCU_RESET, &dev->phy.state))
- mt76_set_channel(phy, &phy->main_chandef, false);
+ if (phy->chanctx && phy->main_chandef.chan && phy->offchannel &&
+ !test_bit(MT76_MCU_RESET, &dev->phy.state)) {
+ __mt76_set_channel(phy, &phy->main_chandef, false);
+ mt76_offchannel_notify(phy, false);
+ }
mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link);
phy->roc_vif = NULL;
phy->roc_link = NULL;
@@ -364,12 +362,15 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct mt76_phy *phy = hw->priv;
struct mt76_dev *dev = phy->dev;
struct mt76_vif_link *mlink;
+ bool offchannel;
int ret = 0;
phy = dev->band_phys[chan->band];
if (!phy)
return -EINVAL;
+ cancel_delayed_work_sync(&phy->mac_work);
+
mutex_lock(&dev->mutex);
if (phy->roc_vif || dev->scan.phy == phy ||
@@ -387,8 +388,18 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mlink->mvif->roc_phy = phy;
phy->roc_vif = vif;
phy->roc_link = mlink;
- cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
- mt76_set_channel(phy, &chandef, true);
+
+ offchannel = mt76_offchannel_chandef(phy, chan, &chandef);
+ if (offchannel)
+ mt76_offchannel_notify(phy, true);
+ ret = __mt76_set_channel(phy, &chandef, offchannel);
+ if (ret) {
+ mlink->mvif->roc_phy = NULL;
+ phy->roc_vif = NULL;
+ phy->roc_link = NULL;
+ mt76_put_vif_phy_link(phy, vif, mlink);
+ goto out;
+ }
ieee80211_ready_on_channel(hw);
ieee80211_queue_delayed_work(phy->hw, &phy->roc_work,
msecs_to_jiffies(duration));
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index f240016ed9f0..f8c2fe5f2f58 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -6,6 +6,7 @@
#include <linux/dma-mapping.h>
#include "mt76.h"
#include "dma.h"
+#include "mt76_connac.h"
static struct mt76_txwi_cache *
mt76_alloc_txwi(struct mt76_dev *dev)
@@ -188,16 +189,18 @@ mt76_dma_queue_magic_cnt_init(struct mt76_dev *dev, struct mt76_queue *q)
static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
- Q_WRITE(q, desc_base, q->desc_dma);
- if ((q->flags & MT_QFLAG_WED_RRO_EN) && !mt76_npu_device_active(dev))
+ if ((q->flags & MT_QFLAG_WED_RRO_EN) &&
+ (!is_mt7992(dev) || !mt76_npu_device_active(dev)))
Q_WRITE(q, ring_size, MT_DMA_RRO_EN | q->ndesc);
else
Q_WRITE(q, ring_size, q->ndesc);
if (mt76_queue_is_npu_tx(q)) {
- writel(q->desc_dma, &q->regs->desc_base);
writel(q->ndesc, &q->regs->ring_size);
+ writel(q->desc_dma, &q->regs->desc_base);
}
+
+ Q_WRITE(q, desc_base, q->desc_dma);
q->head = Q_READ(q, dma_idx);
q->tail = q->head;
}
@@ -663,6 +666,8 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
if (!t)
goto free_skb;
+ t->phy_idx = phy->band_idx;
+ t->qid = qid;
txwi = mt76_get_txwi_ptr(dev, t);
skb->prev = skb->next = NULL;
@@ -874,7 +879,16 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
if (!buf)
break;
- if (!mt76_queue_is_wed_rro(q))
+ if (mtk_wed_device_active(&dev->mmio.wed) &&
+ mt76_queue_is_wed_rro(q))
+ continue;
+
+ if (mt76_npu_device_active(dev) &&
+ mt76_queue_is_wed_rro(q))
+ continue;
+
+ if (!mt76_queue_is_wed_rro_rxdmad_c(q) &&
+ !mt76_queue_is_wed_rro_ind(q))
mt76_put_page_pool_buf(buf, false);
} while (1);
@@ -915,6 +929,13 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
mt76_queue_is_wed_rro(q))
return;
+ if (mt76_npu_device_active(dev) &&
+ mt76_queue_is_wed_rro(q))
+ return;
+
+ if (mt76_queue_is_npu_txfree(q))
+ return;
+
mt76_dma_sync_idx(dev, q);
if (mt76_queue_is_npu(q))
mt76_npu_fill_rx_queue(dev, q);
@@ -1168,10 +1189,6 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
- if (mtk_wed_device_active(&dev->mmio.wed) &&
- mt76_queue_is_wed_rro(q))
- continue;
-
netif_napi_del(&dev->napi[i]);
mt76_dma_rx_cleanup(dev, q);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h
index 4a63de6c5bf5..2a0226c83f3c 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.h
+++ b/drivers/net/wireless/mediatek/mt76/dma.h
@@ -174,7 +174,9 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
static inline void
mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
- dev->queue_ops->reset_q(dev, q, true);
+ bool reset_idx = q && !mt76_queue_is_npu_tx(q);
+
+ dev->queue_ops->reset_q(dev, q, reset_idx);
if (mtk_wed_device_active(&dev->mmio.wed))
mt76_wed_dma_setup(dev, q, true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 573400d57ce7..afdb73661866 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -9,6 +9,13 @@
#include <linux/nvmem-consumer.h>
#include <linux/etherdevice.h>
#include "mt76.h"
+#include "mt76_connac.h"
+
+enum mt76_sku_type {
+ MT76_SKU_RATE,
+ MT76_SKU_BACKOFF,
+ MT76_SKU_BACKOFF_BF_OFFSET,
+};
static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len)
{
@@ -292,7 +299,6 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
}
EXPORT_SYMBOL_GPL(mt76_find_channel_node);
-
static s8
mt76_get_txs_delta(struct device_node *np, u8 nss)
{
@@ -306,9 +312,24 @@ mt76_get_txs_delta(struct device_node *np, u8 nss)
return be32_to_cpu(val[nss - 1]);
}
+static inline u8 mt76_backoff_n_chains(struct mt76_dev *dev, u8 idx)
+{
+ /* 0:1T1ss, 1:2T1ss, ..., 14:5T5ss */
+ static const u8 connac3_table[] = {
+ 1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5};
+ static const u8 connac2_table[] = {
+ 1, 2, 3, 4, 2, 3, 4, 3, 4, 4, 0, 0, 0, 0, 0};
+
+ if (idx >= ARRAY_SIZE(connac3_table))
+ return 0;
+
+ return is_mt799x(dev) ? connac3_table[idx] : connac2_table[idx];
+}
+
static void
-mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
- s8 target_power, s8 nss_delta, s8 *max_power)
+mt76_apply_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len,
+ const s8 *data, s8 target_power, s8 nss_delta,
+ s8 *max_power, int n_chains, enum mt76_sku_type type)
{
int i;
@@ -316,18 +337,51 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
return;
for (i = 0; i < pwr_len; i++) {
- pwr[i] = min_t(s8, target_power, data[i] + nss_delta);
+ u8 backoff_chain_idx = i;
+ int backoff_n_chains;
+ s8 backoff_delta;
+ s8 delta;
+
+ switch (type) {
+ case MT76_SKU_RATE:
+ delta = 0;
+ backoff_delta = 0;
+ backoff_n_chains = 0;
+ break;
+ case MT76_SKU_BACKOFF_BF_OFFSET:
+ backoff_chain_idx += 1;
+ fallthrough;
+ case MT76_SKU_BACKOFF:
+ delta = mt76_tx_power_path_delta(n_chains);
+ backoff_n_chains = mt76_backoff_n_chains(dev, backoff_chain_idx);
+ backoff_delta = mt76_tx_power_path_delta(backoff_n_chains);
+ break;
+ default:
+ return;
+ }
+
+ pwr[i] = min_t(s8, target_power + delta - backoff_delta, data[i] + nss_delta);
+
+ /* used for padding, doesn't need to be considered */
+ if (data[i] >= S8_MAX - 1)
+ continue;
+
+ /* only consider backoff value for the configured chain number */
+ if (type != MT76_SKU_RATE && n_chains != backoff_n_chains)
+ continue;
+
*max_power = max(*max_power, pwr[i]);
}
}
static void
-mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- const s8 *data, size_t len, s8 target_power,
- s8 nss_delta)
+mt76_apply_multi_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len,
+ s8 pwr_num, const s8 *data, size_t len,
+ s8 target_power, s8 nss_delta, s8 *max_power,
+ int n_chains, enum mt76_sku_type type)
{
+ static const int connac2_backoff_ru_idx = 2;
int i, cur;
- s8 max_power = -128;
if (!data)
return;
@@ -337,8 +391,26 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
if (len < pwr_len + 1)
break;
- mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
- target_power, nss_delta, &max_power);
+ /* Each RU entry (RU26, RU52, RU106, BW20, ...) in the DTS
+ * corresponds to 10 stream combinations (1T1ss, 2T1ss, 3T1ss,
+ * 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss, 4T3ss, 4T4ss).
+ *
+ * For beamforming tables:
+ * - In connac2, beamforming entries for BW20~BW160 and OFDM
+ * do not include 1T1ss.
+ * - In connac3, beamforming entries for BW20~BW160 and RU
+ * include 1T1ss, but OFDM beamforming does not include 1T1ss.
+ *
+ * Non-beamforming and RU entries for both connac2 and connac3
+ * include 1T1ss.
+ */
+ if (!is_mt799x(dev) && type == MT76_SKU_BACKOFF &&
+ i > connac2_backoff_ru_idx)
+ type = MT76_SKU_BACKOFF_BF_OFFSET;
+
+ mt76_apply_array_limit(dev, pwr + pwr_len * i, pwr_len, data + 1,
+ target_power, nss_delta, max_power,
+ n_chains, type);
if (--cur > 0)
continue;
@@ -360,18 +432,11 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
struct device_node *np;
const s8 *val;
char name[16];
- u32 mcs_rates = dev->drv->mcs_rates;
- u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
char band;
size_t len;
- s8 max_power = 0;
- s8 max_power_backoff = -127;
+ s8 max_power = -127;
s8 txs_delta;
int n_chains = hweight16(phy->chainmask);
- s8 target_power_combine = target_power + mt76_tx_power_path_delta(n_chains);
-
- if (!mcs_rates)
- mcs_rates = 10;
memset(dest, target_power, sizeof(*dest) - sizeof(dest->path));
memset(&dest->path, 0, sizeof(dest->path));
@@ -409,46 +474,45 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask));
val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
- mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
- target_power, txs_delta, &max_power);
+ mt76_apply_array_limit(dev, dest->cck, ARRAY_SIZE(dest->cck), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE);
- val = mt76_get_of_array_s8(np, "rates-ofdm",
- &len, ARRAY_SIZE(dest->ofdm));
- mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
- target_power, txs_delta, &max_power);
+ val = mt76_get_of_array_s8(np, "rates-ofdm", &len, ARRAY_SIZE(dest->ofdm));
+ mt76_apply_array_limit(dev, dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE);
- val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1);
- mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
- ARRAY_SIZE(dest->mcs), val, len,
- target_power, txs_delta);
+ val = mt76_get_of_array_s8(np, "rates-mcs", &len, ARRAY_SIZE(dest->mcs[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
+ ARRAY_SIZE(dest->mcs), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_RATE);
- val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1);
- mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
- ARRAY_SIZE(dest->ru), val, len,
- target_power, txs_delta);
+ val = mt76_get_of_array_s8(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->ru[0], ARRAY_SIZE(dest->ru[0]),
+ ARRAY_SIZE(dest->ru), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_RATE);
- max_power_backoff = max_power;
val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck));
- mt76_apply_array_limit(dest->path.cck, ARRAY_SIZE(dest->path.cck), val,
- target_power_combine, txs_delta, &max_power_backoff);
+ mt76_apply_array_limit(dev, dest->path.cck, ARRAY_SIZE(dest->path.cck), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm));
- mt76_apply_array_limit(dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val,
- target_power_combine, txs_delta, &max_power_backoff);
+ mt76_apply_array_limit(dev, dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf));
- mt76_apply_array_limit(dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val,
- target_power_combine, txs_delta, &max_power_backoff);
+ mt76_apply_array_limit(dev, dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val,
+ target_power, txs_delta, &max_power, n_chains,
+ MT76_SKU_BACKOFF_BF_OFFSET);
val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1);
- mt76_apply_multi_array_limit(dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]),
- ARRAY_SIZE(dest->path.ru), val, len,
- target_power_combine, txs_delta);
+ mt76_apply_multi_array_limit(dev, dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]),
+ ARRAY_SIZE(dest->path.ru), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1);
- mt76_apply_multi_array_limit(dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]),
- ARRAY_SIZE(dest->path.ru_bf), val, len,
- target_power_combine, txs_delta);
+ mt76_apply_multi_array_limit(dev, dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]),
+ ARRAY_SIZE(dest->path.ru_bf), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
return max_power;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 75772979f438..4ae5e4715a9c 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -726,6 +726,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
INIT_LIST_HEAD(&dev->rxwi_cache);
dev->token_size = dev->drv->token_size;
INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work);
+ spin_lock_init(&dev->scan_lock);
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
skb_queue_head_init(&dev->rx_skb[i]);
@@ -970,6 +971,9 @@ bool mt76_has_tx_pending(struct mt76_phy *phy)
return true;
}
+ if (atomic_read(&phy->mgmt_tx_pending))
+ return true;
+
return false;
}
EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
@@ -1030,9 +1034,10 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
int timeout = HZ / 5;
int ret;
- set_bit(MT76_RESET, &phy->state);
-
mt76_worker_disable(&dev->tx_worker);
+ mt76_txq_schedule_pending(phy);
+
+ set_bit(MT76_RESET, &phy->state);
wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
mt76_update_survey(phy);
@@ -1716,6 +1721,16 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
idr_destroy(&wcid->pktid);
+ /* Remove from sta_poll_list to prevent list corruption after reset.
+ * Without this, mt76_reset_device() reinitializes sta_poll_list but
+ * leaves wcid->poll_list with stale pointers, causing list corruption
+ * when mt76_wcid_add_poll() checks list_empty().
+ */
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (!list_empty(&wcid->poll_list))
+ list_del_init(&wcid->poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
spin_lock_bh(&phy->tx_lock);
if (!list_empty(&wcid->tx_list))
@@ -2121,3 +2136,214 @@ u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links)
return sel_links;
}
EXPORT_SYMBOL_GPL(mt76_select_links);
+
+struct mt76_offchannel_cb_data {
+ struct mt76_phy *phy;
+ bool offchannel;
+};
+
+static void
+mt76_offchannel_send_nullfunc(struct mt76_offchannel_cb_data *data,
+ struct ieee80211_vif *vif, int link_id)
+{
+ struct mt76_phy *phy = data->phy;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_sta *sta = NULL;
+ struct ieee80211_hdr *hdr;
+ struct mt76_wcid *wcid;
+ struct sk_buff *skb;
+
+ skb = ieee80211_nullfunc_get(phy->hw, vif, link_id, true);
+ if (!skb)
+ return;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (data->offchannel)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ skb->priority = 7;
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+ if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb,
+ phy->main_chandef.chan->band,
+ &sta))
+ return;
+
+ if (sta)
+ wcid = (struct mt76_wcid *)sta->drv_priv;
+ else
+ wcid = ((struct mt76_vif_link *)vif->drv_priv)->wcid;
+
+ if (link_id >= 0) {
+ info = IEEE80211_SKB_CB(skb);
+ info->control.flags &= ~IEEE80211_TX_CTRL_MLO_LINK;
+ info->control.flags |=
+ u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK);
+ }
+
+ mt76_tx(phy, sta, wcid, skb);
+}
+
+static void
+mt76_offchannel_notify_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct mt76_offchannel_cb_data *data = _data;
+ struct mt76_vif_link *mlink;
+ struct mt76_vif_data *mvif;
+ int link_id;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ mlink = (struct mt76_vif_link *)vif->drv_priv;
+ mvif = mlink->mvif;
+
+ if (!ieee80211_vif_is_mld(vif)) {
+ if (mt76_vif_link_phy(mlink) == data->phy) {
+ if (!data->offchannel && mlink->beacon_mon_interval)
+ WRITE_ONCE(mlink->beacon_mon_last, jiffies);
+ mt76_offchannel_send_nullfunc(data, vif, -1);
+ }
+ return;
+ }
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ if (link_id == mvif->deflink_id)
+ mlink = (struct mt76_vif_link *)vif->drv_priv;
+ else
+ mlink = rcu_dereference(mvif->link[link_id]);
+ if (!mlink)
+ continue;
+ if (mt76_vif_link_phy(mlink) != data->phy)
+ continue;
+
+ if (!data->offchannel && mlink->beacon_mon_interval)
+ WRITE_ONCE(mlink->beacon_mon_last, jiffies);
+
+ mt76_offchannel_send_nullfunc(data, vif, link_id);
+ }
+}
+
+void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel)
+{
+ struct mt76_offchannel_cb_data data = {
+ .phy = phy,
+ .offchannel = offchannel,
+ };
+
+ if (!phy->num_sta)
+ return;
+
+ local_bh_disable();
+ ieee80211_iterate_active_interfaces_atomic(phy->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ mt76_offchannel_notify_iter, &data);
+ local_bh_enable();
+}
+EXPORT_SYMBOL_GPL(mt76_offchannel_notify);
+
+struct mt76_rx_beacon_data {
+ struct mt76_phy *phy;
+ const u8 *bssid;
+};
+
+static void mt76_rx_beacon_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt76_rx_beacon_data *data = _data;
+ struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+ struct mt76_vif_data *mvif = mlink->mvif;
+ int link_id;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ struct ieee80211_bss_conf *link_conf;
+
+ if (link_id == mvif->deflink_id)
+ mlink = (struct mt76_vif_link *)vif->drv_priv;
+ else
+ mlink = rcu_dereference(mvif->link[link_id]);
+ if (!mlink || !mlink->beacon_mon_interval)
+ continue;
+
+ if (mt76_vif_link_phy(mlink) != data->phy)
+ continue;
+
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ if (!link_conf)
+ continue;
+
+ if (!ether_addr_equal(link_conf->bssid, data->bssid) &&
+ (!link_conf->nontransmitted ||
+ !ether_addr_equal(link_conf->transmitter_bssid,
+ data->bssid)))
+ continue;
+
+ WRITE_ONCE(mlink->beacon_mon_last, jiffies);
+ }
+}
+
+void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
+ struct mt76_rx_beacon_data data = {
+ .phy = phy,
+ .bssid = hdr->addr3,
+ };
+
+ mt76_scan_rx_beacon(phy->dev, phy->chandef.chan);
+
+ if (!phy->num_sta)
+ return;
+
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_ONLY_MONITOR))
+ return;
+
+ ieee80211_iterate_active_interfaces_atomic(phy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt76_rx_beacon_iter, &data);
+}
+EXPORT_SYMBOL_GPL(mt76_rx_beacon);
+
+static void mt76_beacon_mon_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt76_phy *phy = data;
+ struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+ struct mt76_vif_data *mvif = mlink->mvif;
+ int link_id;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return;
+
+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
+ if (link_id == mvif->deflink_id)
+ mlink = (struct mt76_vif_link *)vif->drv_priv;
+ else
+ mlink = rcu_dereference(mvif->link[link_id]);
+ if (!mlink || !mlink->beacon_mon_interval)
+ continue;
+
+ if (mt76_vif_link_phy(mlink) != phy)
+ continue;
+
+ if (time_after(jiffies,
+ READ_ONCE(mlink->beacon_mon_last) +
+ MT76_BEACON_MON_MAX_MISS * mlink->beacon_mon_interval))
+ ieee80211_beacon_loss(vif);
+ }
+}
+
+void mt76_beacon_mon_check(struct mt76_phy *phy)
+{
+ if (phy->offchannel)
+ return;
+
+ ieee80211_iterate_active_interfaces_atomic(phy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt76_beacon_mon_iter, phy);
+}
+EXPORT_SYMBOL_GPL(mt76_beacon_mon_check);
diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c
index 535c3d8a9cc0..cbfb3bbec503 100644
--- a/drivers/net/wireless/mediatek/mt76/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mcu.c
@@ -98,7 +98,7 @@ retry:
/* orig skb might be needed for retry, mcu_skb_send_msg consumes it */
if (orig_skb)
skb_get(orig_skb);
- ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
+ ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, wait_resp ? &seq : NULL);
if (ret < 0)
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index d05e83ea1cac..527bef97e122 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -55,6 +55,8 @@
FIELD_PREP(MT_QFLAG_WED_RING, _n))
#define MT_NPU_Q_TX(_n) __MT_NPU_Q(MT76_WED_Q_TX, _n)
#define MT_NPU_Q_RX(_n) __MT_NPU_Q(MT76_WED_Q_RX, _n)
+#define MT_NPU_Q_TXFREE(_n) (FIELD_PREP(MT_QFLAG_WED_TYPE, MT76_WED_Q_TXFREE) | \
+ FIELD_PREP(MT_QFLAG_WED_RING, _n))
struct mt76_dev;
struct mt76_phy;
@@ -362,6 +364,7 @@ enum mt76_wcid_flags {
};
#define MT76_N_WCIDS 1088
+#define MT76_BEACON_MON_MAX_MISS 7
/* stored in ieee80211_tx_info::hw_queue */
#define MT_TX_HW_QUEUE_PHY GENMASK(3, 2)
@@ -448,6 +451,7 @@ struct mt76_txwi_cache {
};
u8 qid;
+ u8 phy_idx;
};
struct mt76_rx_tid {
@@ -540,7 +544,6 @@ struct mt76_driver_ops {
u32 survey_flags;
u16 txwi_size;
u16 token_size;
- u8 mcs_rates;
unsigned int link_data_size;
@@ -831,6 +834,8 @@ struct mt76_vif_link {
u8 mcast_rates_idx;
u8 beacon_rates_idx;
bool offchannel;
+ unsigned long beacon_mon_last;
+ u16 beacon_mon_interval;
struct ieee80211_chanctx_conf *ctx;
struct mt76_wcid *wcid;
struct mt76_vif_data *mvif;
@@ -859,6 +864,8 @@ struct mt76_phy {
struct list_head tx_list;
struct mt76_queue *q_tx[__MT_TXQ_MAX];
+ atomic_t mgmt_tx_pending;
+
struct cfg80211_chan_def chandef;
struct cfg80211_chan_def main_chandef;
bool offchannel;
@@ -1002,6 +1009,7 @@ struct mt76_dev {
u32 rxfilter;
struct delayed_work scan_work;
+ spinlock_t scan_lock;
struct {
struct cfg80211_scan_request *req;
struct ieee80211_channel *chan;
@@ -1009,6 +1017,8 @@ struct mt76_dev {
struct mt76_vif_link *mlink;
struct mt76_phy *phy;
int chan_idx;
+ bool beacon_wait;
+ bool beacon_received;
} scan;
#ifdef CONFIG_NL80211_TESTMODE
@@ -1518,6 +1528,7 @@ void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta,
void mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb);
void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid);
void mt76_txq_schedule_all(struct mt76_phy *phy);
+void mt76_txq_schedule_pending(struct mt76_phy *phy);
void mt76_tx_worker_run(struct mt76_dev *dev);
void mt76_tx_worker(struct mt76_worker *w);
void mt76_release_buffered_frames(struct ieee80211_hw *hw,
@@ -1596,6 +1607,9 @@ int mt76_get_rate(struct mt76_dev *dev,
int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req);
void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan);
+void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb);
+void mt76_beacon_mon_check(struct mt76_phy *phy);
void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const u8 *mac);
void mt76_sw_scan_complete(struct ieee80211_hw *hw,
@@ -1649,6 +1663,9 @@ void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index);
int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct net_device *dev, enum tc_setup_type type,
void *type_data);
+int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex,
+ u32 direction, u32 i_count_addr,
+ u32 o_status_addr, u32 o_count_addr);
#else
static inline void mt76_npu_check_ppe(struct mt76_dev *dev,
struct sk_buff *skb, u32 info)
@@ -1707,6 +1724,13 @@ static inline int mt76_npu_net_setup_tc(struct ieee80211_hw *hw,
{
return -EOPNOTSUPP;
}
+
+static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex,
+ u32 direction, u32 i_count_addr,
+ u32 o_status_addr, u32 o_count_addr)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_MT76_NPU */
static inline bool mt76_npu_device_active(struct mt76_dev *dev)
@@ -1775,6 +1799,18 @@ void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_entry *e);
int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
bool offchannel);
+
+static inline bool
+mt76_offchannel_chandef(struct mt76_phy *phy, struct ieee80211_channel *chan,
+ struct cfg80211_chan_def *chandef)
+{
+ cfg80211_chandef_create(chandef, chan, NL80211_CHAN_HT20);
+ if (phy->main_chandef.chan != chan)
+ return true;
+
+ *chandef = phy->main_chandef;
+ return false;
+}
int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
bool offchannel);
void mt76_scan_work(struct work_struct *work);
@@ -1786,6 +1822,7 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy,
struct ieee80211_vif *vif);
void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct mt76_vif_link *mlink);
+void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel);
/* usb */
static inline bool mt76u_urb_error(struct urb *urb)
@@ -1993,6 +2030,14 @@ static inline bool mt76_queue_is_npu_rx(struct mt76_queue *q)
FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
}
+static inline bool mt76_queue_is_npu_txfree(struct mt76_queue *q)
+{
+ if (q->flags & MT_QFLAG_WED)
+ return false;
+
+ return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE;
+}
+
struct mt76_txwi_cache *
mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index 45992fdcec60..ce0051468501 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -1167,21 +1167,6 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta,
}
EXPORT_SYMBOL_GPL(mt7615_mac_set_rates);
-void mt7615_mac_enable_rtscts(struct mt7615_dev *dev,
- struct ieee80211_vif *vif, bool enable)
-{
- struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
- u32 addr;
-
- addr = mt7615_mac_wtbl_addr(dev, mvif->sta.wcid.idx) + 3 * 4;
-
- if (enable)
- mt76_set(dev, addr, MT_WTBL_W3_RTS);
- else
- mt76_clear(dev, addr, MT_WTBL_W3_RTS);
-}
-EXPORT_SYMBOL_GPL(mt7615_mac_enable_rtscts);
-
static int
mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 727266892c3d..fc619acbb40d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -583,9 +583,6 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw,
}
}
- if (changed & BSS_CHANGED_ERP_CTS_PROT)
- mt7615_mac_enable_rtscts(dev, vif, info->use_cts_prot);
-
if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) {
mt7615_mcu_add_bss_info(phy, vif, NULL, true);
mt7615_mcu_sta_add(phy, vif, NULL, true);
@@ -598,6 +595,10 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw,
BSS_CHANGED_BEACON_ENABLED))
mt7615_mcu_add_beacon(dev, hw, vif, info->enable_beacon);
+ if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7615_mcu_set_protection(phy, vif, info->ht_operation_mode,
+ info->use_cts_prot);
+
if (changed & BSS_CHANGED_PS)
mt76_connac_mcu_set_vif_ps(&dev->mt76, vif);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
index fc0054f8bd60..ff57ede87f71 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
@@ -2564,3 +2564,50 @@ int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif,
return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_ROC),
&req, sizeof(req), false);
}
+
+int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ u8 ht_mode, bool use_cts_prot)
+{
+ struct mt7615_dev *dev = phy->dev;
+ struct {
+ u8 prot_idx;
+ u8 band;
+ u8 rsv[2];
+
+ bool long_nav;
+ bool prot_mm;
+ bool prot_gf;
+ bool prot_bw40;
+ bool prot_rifs;
+ bool prot_bw80;
+ bool prot_bw160;
+ u8 prot_erp_mask;
+ } __packed req = {
+ .prot_idx = 0x2,
+ .band = phy != &dev->phy,
+ };
+
+ switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ req.prot_mm = true;
+ req.prot_gf = true;
+ fallthrough;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ req.prot_bw40 = true;
+ break;
+ }
+
+ if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ req.prot_gf = true;
+
+ if (use_cts_prot) {
+ struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
+ u8 i = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->mt76.omac_idx;
+
+ req.prot_erp_mask = BIT(i);
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PROTECT_CTRL), &req,
+ sizeof(req), true);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
index c93fd245c90f..391928405f32 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
@@ -467,8 +467,6 @@ void mt7615_mac_reset_counters(struct mt7615_phy *phy);
void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy);
void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable);
void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy);
-void mt7615_mac_enable_rtscts(struct mt7615_dev *dev,
- struct ieee80211_vif *vif, bool enable);
void mt7615_mac_sta_poll(struct mt7615_dev *dev);
int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid,
@@ -523,7 +521,8 @@ int mt7615_mcu_set_sku_en(struct mt7615_phy *phy, bool enable);
int mt7615_mcu_apply_rx_dcoc(struct mt7615_phy *phy);
int mt7615_mcu_apply_tx_dpd(struct mt7615_phy *phy);
int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy);
-
+int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif,
+ u8 ht_mode, bool use_cts_prot);
int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_channel *chan, int duration);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
index eb3c24d51987..e4133e9181d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
@@ -455,8 +455,6 @@ enum mt7615_reg_base {
#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8)
#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20)
-#define MT_WTBL_W3_RTS BIT(22)
-
#define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5)
#define MT_WTBL_W5_SHORT_GI_20 BIT(8)
#define MT_WTBL_W5_SHORT_GI_40 BIT(9)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 813d61bffc2c..51423c7740bd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -182,14 +182,20 @@ static inline bool is_mt7920(struct mt76_dev *dev)
return mt76_chip(dev) == 0x7920;
}
+static inline bool is_mt7902(struct mt76_dev *dev)
+{
+ return mt76_chip(dev) == 0x7902;
+}
+
static inline bool is_mt7922(struct mt76_dev *dev)
{
return mt76_chip(dev) == 0x7922;
}
-static inline bool is_mt7921(struct mt76_dev *dev)
+static inline bool is_connac2(struct mt76_dev *dev)
{
- return mt76_chip(dev) == 0x7961 || is_mt7922(dev) || is_mt7920(dev);
+ return mt76_chip(dev) == 0x7961 || is_mt7922(dev) || is_mt7920(dev) ||
+ is_mt7902(dev);
}
static inline bool is_mt7663(struct mt76_dev *dev)
@@ -271,6 +277,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev)
case 0x7961:
case 0x7920:
case 0x7922:
+ case 0x7902:
case 0x7925:
case 0x7663:
case 0x7622:
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
index f946ddc20a47..0339e2e7ab60 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
@@ -173,7 +173,7 @@ void mt76_connac_write_hw_txp(struct mt76_dev *dev,
txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID);
- if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev))
+ if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev))
last_mask = MT_TXD_LEN_LAST;
else
last_mask = MT_TXD_LEN_AMSDU_LAST |
@@ -217,7 +217,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev,
u32 last_mask;
int i;
- if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev))
+ if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev))
last_mask = MT_TXD_LEN_LAST;
else
last_mask = MT_TXD_LEN_MSDU_LAST;
@@ -309,7 +309,7 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
chandef = mvif->ctx ? &mvif->ctx->def : &mphy->chandef;
band = chandef->chan->band;
- if (is_mt7921(mphy->dev)) {
+ if (is_connac2(mphy->dev)) {
rateidx = ffs(conf->basic_rates) - 1;
goto legacy;
}
@@ -548,7 +548,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
val = MT_TXD1_LONG_FORMAT |
FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
- if (!is_mt7921(dev))
+ if (!is_connac2(dev))
val |= MT_TXD1_VTA;
if (phy_idx || band_idx)
val |= MT_TXD1_TGID;
@@ -557,7 +557,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
txwi[2] = 0;
val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 15);
- if (!is_mt7921(dev))
+ if (!is_connac2(dev))
val |= MT_TXD3_SW_POWER_MGMT;
if (key)
val |= MT_TXD3_PROTECT_FRAME;
@@ -599,7 +599,7 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
txwi[6] |= cpu_to_le32(val);
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
- if (!is_mt7921(dev)) {
+ if (!is_connac2(dev)) {
u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
if (!spe_idx)
@@ -831,7 +831,7 @@ mt76_connac2_mac_decode_he_mu_radiotap(struct mt76_dev *dev, struct sk_buff *skb
};
struct ieee80211_radiotap_he_mu *he_mu;
- if (is_mt7921(dev)) {
+ if (is_connac2(dev)) {
mu_known.flags1 |= HE_BITS(MU_FLAGS1_SIG_B_COMP_KNOWN);
mu_known.flags2 |= HE_BITS(MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN);
}
@@ -1047,7 +1047,7 @@ int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev,
stbc = FIELD_GET(MT_PRXV_HT_STBC, v0);
gi = FIELD_GET(MT_PRXV_HT_SGI, v0);
*mode = FIELD_GET(MT_PRXV_TX_MODE, v0);
- if (is_mt7921(dev))
+ if (is_connac2(dev))
dcm = !!(idx & MT_PRXV_TX_DCM);
else
dcm = FIELD_GET(MT_PRXV_DCM, v0);
@@ -1153,8 +1153,10 @@ void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
return;
wcid = (struct mt76_wcid *)sta->drv_priv;
- if (!test_and_set_bit(tid, &wcid->ampdu_state))
- ieee80211_start_tx_ba_session(sta, tid, 0);
+ if (!test_and_set_bit(tid, &wcid->ampdu_state)) {
+ if (ieee80211_start_tx_ba_session(sta, tid, 0))
+ clear_bit(tid, &wcid->ampdu_state);
+ }
}
EXPORT_SYMBOL_GPL(mt76_connac2_tx_check_aggr);
@@ -1207,5 +1209,11 @@ void mt76_connac2_tx_token_put(struct mt76_dev *dev)
}
spin_unlock_bh(&dev->token_lock);
idr_destroy(&dev->token);
+
+ for (id = 0; id < __MT_MAX_BAND; id++) {
+ struct mt76_phy *phy = dev->phys[id];
+ if (phy)
+ atomic_set(&phy->mgmt_tx_pending, 0);
+ }
}
EXPORT_SYMBOL_GPL(mt76_connac2_tx_token_put);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 0457712286d5..89bd52ea8bf7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -4,6 +4,7 @@
#include <linux/firmware.h>
#include "mt76_connac2_mac.h"
#include "mt76_connac_mcu.h"
+#include "mt792x_regs.h"
int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option)
{
@@ -65,7 +66,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
int cmd;
if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) ||
- (is_mt7921(dev) && addr == 0x900000) ||
+ (is_connac2(dev) && addr == 0x900000) ||
(is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) ||
(is_mt799x(dev) && addr == 0x900000))
cmd = MCU_CMD(PATCH_START_REQ);
@@ -402,7 +403,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
switch (vif->type) {
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
- if (vif->p2p && !is_mt7921(dev))
+ if (vif->p2p && !is_connac2(dev))
conn_type = CONNECTION_P2P_GC;
else
conn_type = CONNECTION_INFRA_STA;
@@ -410,7 +411,7 @@ void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb,
basic->aid = cpu_to_le16(link_sta->sta->aid);
break;
case NL80211_IFTYPE_STATION:
- if (vif->p2p && !is_mt7921(dev))
+ if (vif->p2p && !is_connac2(dev))
conn_type = CONNECTION_P2P_GO;
else
conn_type = CONNECTION_INFRA_AP;
@@ -874,7 +875,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
struct sta_rec_vht *vht;
int len;
- len = is_mt7921(dev) ? sizeof(*vht) : sizeof(*vht) - 4;
+ len = is_connac2(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->deflink.vht_cap.cap);
@@ -885,7 +886,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
/* starec uapsd */
mt76_connac_mcu_sta_uapsd(skb, vif, sta);
- if (!is_mt7921(dev))
+ if (!is_connac2(dev))
return;
if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)
@@ -1295,8 +1296,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
wtbl_hdr);
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true);
if (ret)
@@ -1309,8 +1312,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx);
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, cmd, true);
}
@@ -1774,7 +1779,7 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
req->ssid_type_ext = n_ssids ? BIT(0) : 0;
req->ssids_num = n_ssids;
- duration = is_mt7921(phy->dev) ? 0 : MT76_CONNAC_SCAN_CHANNEL_TIME;
+ duration = is_connac2(phy->dev) ? 0 : MT76_CONNAC_SCAN_CHANNEL_TIME;
/* increase channel time for passive scan */
if (!sreq->n_ssids)
duration *= 2;
@@ -1817,7 +1822,7 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
req->ies_len = cpu_to_le16(sreq->ie_len);
}
- if (is_mt7921(phy->dev))
+ if (is_connac2(phy->dev))
req->scan_func |= SCAN_FUNC_SPLIT_SCAN;
memcpy(req->bssid, sreq->bssid, ETH_ALEN);
@@ -1893,7 +1898,7 @@ int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy,
get_random_mask_addr(addr, sreq->mac_addr,
sreq->mac_addr_mask);
}
- if (is_mt7921(phy->dev)) {
+ if (is_connac2(phy->dev)) {
req->mt7921.bss_idx = mvif->idx;
req->mt7921.delay = cpu_to_le32(sreq->delay);
}
@@ -2033,7 +2038,7 @@ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
struct mt76_power_limits *limits,
enum nl80211_band band)
{
- int max_power = is_mt7921(dev) ? 127 : 63;
+ int max_power = is_connac2(dev) ? 127 : 63;
int i, offset = sizeof(limits->cck);
memset(sku, max_power, MT_SKU_POWER_LIMIT);
@@ -2061,7 +2066,7 @@ mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku,
offset += 12;
}
- if (!is_mt7921(dev))
+ if (!is_connac2(dev))
return;
/* he */
@@ -2117,7 +2122,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
enum nl80211_band band)
{
struct mt76_dev *dev = phy->dev;
- int sku_len, batch_len = is_mt7921(dev) ? 8 : 16;
+ int sku_len, batch_len = is_connac2(dev) ? 8 : 16;
static const u8 chan_list_2ghz[] = {
1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14
@@ -2158,7 +2163,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
if (!limits)
return -ENOMEM;
- sku_len = is_mt7921(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92;
+ sku_len = is_connac2(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92;
tx_power = 2 * phy->hw->conf.power_level;
if (!tx_power)
tx_power = 127;
@@ -2242,6 +2247,9 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
false);
if (err < 0)
goto out;
+
+ /* read a CR to avoid PSE buffer underflow */
+ mt76_connac_mcu_reg_rr(dev, MT_PSE_BASE);
}
out:
@@ -2764,12 +2772,16 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
return PTR_ERR(skb);
ret = mt76_connac_mcu_sta_key_tlv(sta_key_conf, skb, key, cmd);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
@@ -3072,7 +3084,7 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info)
{
u32 mode = DL_MODE_NEED_RSP;
- if ((!is_mt7921(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT)
+ if ((!is_connac2(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT)
return mode;
switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 8d59cf43f0e2..ac5126ab68ff 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -628,6 +628,13 @@ struct sta_rec_tx_proc {
__le32 flag;
} __packed;
+struct sta_rec_eml_op {
+ __le16 tag;
+ __le16 len;
+ u8 link_bitmap;
+ u8 link_ant_num[3];
+} __packed;
+
/* wtbl_rec */
struct wtbl_req_hdr {
@@ -796,6 +803,7 @@ struct wtbl_raw {
sizeof(struct sta_rec_he_6g_capa) + \
sizeof(struct sta_rec_pn_info) + \
sizeof(struct sta_rec_tx_proc) + \
+ sizeof(struct sta_rec_eml_op) + \
sizeof(struct tlv) + \
MT76_CONNAC_WTBL_UPDATE_MAX_SIZE)
@@ -832,6 +840,7 @@ enum {
STA_REC_PN_INFO = 0x26,
STA_REC_KEY_V3 = 0x27,
STA_REC_HDRT = 0x28,
+ STA_REC_EML_OP = 0x29,
STA_REC_HDR_TRANS = 0x2B,
STA_REC_MAX_NUM
};
@@ -1308,7 +1317,9 @@ enum {
MCU_UNI_CMD_PER_STA_INFO = 0x6d,
MCU_UNI_CMD_ALL_STA_INFO = 0x6e,
MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
+ MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74,
MCU_UNI_CMD_RADIO_STATUS = 0x80,
+ MCU_UNI_CMD_MLD = 0x82,
MCU_UNI_CMD_SDO = 0x88,
};
@@ -1363,6 +1374,7 @@ enum {
UNI_BSS_INFO_BASIC = 0,
UNI_BSS_INFO_RA = 1,
UNI_BSS_INFO_RLM = 2,
+ UNI_BSS_INFO_PROTECT_INFO = 3,
UNI_BSS_INFO_BSS_COLOR = 4,
UNI_BSS_INFO_HE_BASIC = 5,
UNI_BSS_INFO_11V_MBSSID = 6,
@@ -1383,6 +1395,7 @@ enum {
UNI_BSS_INFO_MLD = 26,
UNI_BSS_INFO_PM_DISABLE = 27,
UNI_BSS_INFO_EHT = 30,
+ UNI_BSS_INFO_MLD_LINK_OP = 36,
};
enum {
@@ -1865,7 +1878,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa)
ret |= feature_set & FW_FEATURE_SET_ENCRYPT ?
DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0;
- if (is_mt7921(dev) || is_mt7925(dev))
+ if (is_connac2(dev) || is_mt7925(dev))
ret |= feature_set & FW_FEATURE_ENCRY_MODE ?
DL_CONFIG_ENCRY_MODE_SEL : 0;
ret |= FIELD_PREP(DL_MODE_KEY_IDX,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index dd71c1c95cc9..dc7c03d23123 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -534,6 +534,7 @@ void mt76x02_reconfig_complete(struct ieee80211_hw *hw,
return;
clear_bit(MT76_RESTART, &dev->mphy.state);
+ ieee80211_wake_queues(hw);
}
EXPORT_SYMBOL_GPL(mt76x02_reconfig_complete);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 22443cbc74ad..250c2d2479b0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -1294,6 +1294,7 @@ free_phy2:
void mt7915_unregister_device(struct mt7915_dev *dev)
{
+ cancel_work_sync(&dev->dump_work);
mt7915_unregister_ext_phy(dev);
mt7915_coredump_unregister(dev);
mt7915_unregister_thermal(&dev->phy);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index cefe56c05731..cec2c4208255 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -232,19 +232,6 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
rcu_read_unlock();
}
-void mt7915_mac_enable_rtscts(struct mt7915_dev *dev,
- struct ieee80211_vif *vif, bool enable)
-{
- struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
- u32 addr;
-
- addr = mt7915_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5);
- if (enable)
- mt76_set(dev, addr, BIT(5));
- else
- mt76_clear(dev, addr, BIT(5));
-}
-
static void
mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q,
struct mt7915_sta *msta, struct sk_buff *skb,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index 90d5e79fbf74..e1d83052aa6d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -68,7 +68,7 @@ int mt7915_run(struct ieee80211_hw *hw)
if (ret)
goto out;
- ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b,
+ ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, MT7915_RTS_LEN_THRES,
phy->mt76->band_idx);
if (ret)
goto out;
@@ -633,8 +633,9 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
if (set_sta == 1)
mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, false);
- if (changed & BSS_CHANGED_ERP_CTS_PROT)
- mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot);
+ if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7915_mcu_set_protection(phy, vif, info->ht_operation_mode,
+ info->use_cts_prot);
if (changed & BSS_CHANGED_ERP_SLOT) {
int slottime = 9;
@@ -851,8 +852,10 @@ int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
return mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_PORT_SECURE, false);
case MT76_STA_EVENT_DISASSOC:
+ mutex_lock(&dev->mt76.mutex);
for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
mt7915_mac_twt_teardown_flow(dev, msta, i);
+ mutex_unlock(&dev->mt76.mutex);
mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_DISCONNECT, false);
msta->wcid.sta_disabled = 1;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 2d2f34aa465d..318c38149463 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -1765,8 +1765,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
}
out:
ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true);
@@ -3954,6 +3956,68 @@ out:
return ret;
}
+int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ u8 ht_mode, bool use_cts_prot)
+{
+ struct mt7915_dev *dev = phy->dev;
+ int len = sizeof(struct sta_req_hdr) + sizeof(struct bss_info_prot);
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+ struct bss_info_prot *prot;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ enum {
+ PROT_NONMEMBER = BIT(1),
+ PROT_20MHZ = BIT(2),
+ PROT_NONHT_MIXED = BIT(3),
+ PROT_LEGACY_ERP = BIT(5),
+ PROT_NONGF_STA = BIT(7),
+ };
+ u32 rts_threshold;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+ NULL, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_PROTECT_INFO,
+ sizeof(*prot));
+ prot = (struct bss_info_prot *)tlv;
+
+ switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ prot->prot_mode = cpu_to_le32(PROT_NONMEMBER);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ prot->prot_mode = cpu_to_le32(PROT_20MHZ);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED);
+ break;
+ }
+
+ if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA);
+
+ if (use_cts_prot)
+ prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP);
+
+ /* reuse current RTS setting */
+ rts_threshold = phy->mt76->hw->wiphy->rts_threshold;
+ if (rts_threshold == (u32)-1)
+ prot->rts_len_thres = cpu_to_le32(MT7915_RTS_LEN_THRES);
+ else
+ prot->rts_len_thres = cpu_to_le32(rts_threshold);
+
+ prot->rts_pkt_thres = 0x2;
+
+ prot->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
+ if (!prot->he_rts_thres)
+ prot->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_EXT_CMD(BSS_INFO_UPDATE), true);
+}
+
int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct cfg80211_he_bss_color *he_bss_color)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 3af11a075a2f..22f73a5ed425 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -399,6 +399,17 @@ struct bss_info_inband_discovery {
__le16 prob_rsp_len;
} __packed __aligned(4);
+struct bss_info_prot {
+ __le16 tag;
+ __le16 len;
+ __le32 prot_type;
+ __le32 prot_mode;
+ __le32 rts_len_thres;
+ __le16 he_rts_thres;
+ u8 rts_pkt_thres;
+ u8 rsv[5];
+} __packed;
+
enum {
BSS_INFO_BCN_CSA,
BSS_INFO_BCN_BCC,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index b5c06201b707..bf1d915a3ca2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -84,6 +84,8 @@
#define MT7915_CRIT_TEMP 110
#define MT7915_MAX_TEMP 120
+#define MT7915_RTS_LEN_THRES 0x92b
+
struct mt7915_vif;
struct mt7915_sta;
struct mt7915_dfs_pulse;
@@ -473,6 +475,8 @@ int mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *v
u32 changed);
int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int enable, u32 changed);
+int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ u8 ht_mode, bool use_cts_prot);
int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_he_obss_pd *he_obss_pd);
int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
index 29732315af1c..8e7790702191 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
@@ -302,7 +302,9 @@ int mt7921_register_device(struct mt792x_dev *dev)
dev->pm.idle_timeout = MT792x_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
- if (!mt76_is_usb(&dev->mt76)) {
+
+ if (!mt76_is_usb(&dev->mt76) &&
+ !is_mt7902(&dev->mt76)) {
dev->pm.enable_user = true;
dev->pm.enable = true;
dev->pm.ds_enable_user = true;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 5fae9a6e273c..3d74fabe7408 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -371,12 +371,15 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
+ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ return;
+
timer_delete_sync(&phy->roc_timer);
- cancel_work_sync(&phy->roc_work);
- if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
- ieee80211_iterate_interfaces(mt76_hw(dev),
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7921_roc_iter, (void *)phy);
+ cancel_work(&phy->roc_work);
+
+ ieee80211_iterate_interfaces(mt76_hw(dev),
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7921_roc_iter, (void *)phy);
}
EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync);
@@ -387,10 +390,11 @@ void mt7921_roc_work(struct work_struct *work)
phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
roc_work);
- if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
- return;
-
mt792x_mutex_acquire(phy->dev);
+ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) {
+ mt792x_mutex_release(phy->dev);
+ return;
+ }
ieee80211_iterate_active_interfaces(phy->mt76->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7921_roc_iter, phy);
@@ -796,7 +800,8 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add)
}
out:
- mt7921_mcu_set_clc(dev, dev->mt76.alpha2, dev->country_ie_env);
+ if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ)
+ mt7921_regd_update(dev);
}
int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
@@ -807,6 +812,9 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
int ret, idx;
+ if (sta->aid > MT7921_MAX_AID)
+ return -ENOENT;
+
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
if (idx < 0)
return -ENOSPC;
@@ -850,6 +858,9 @@ int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ if (sta->aid > MT7921_MAX_AID)
+ return -ENOENT;
+
if (ev != MT76_STA_EVENT_ASSOC)
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 833d0ab64230..8442dbd2ee23 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -1353,6 +1353,9 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
u16 len = le16_to_cpu(rule->len);
u16 offset = len + sizeof(*rule);
+ if (buf_len < offset)
+ break;
+
pos += offset;
buf_len -= offset;
if (rule->alpha2[0] != alpha2[0] ||
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
index 83fc7f49ff84..64f60c4fc60c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
@@ -7,6 +7,8 @@
#include "../mt792x.h"
#include "regs.h"
+#define MT7921_MAX_AID 20
+
#define MT7921_TX_RING_SIZE 2048
#define MT7921_TX_MCU_RING_SIZE 256
#define MT7921_TX_FWDL_RING_SIZE 128
@@ -15,6 +17,9 @@
#define MT7921_RX_MCU_RING_SIZE 8
#define MT7921_RX_MCU_WA_RING_SIZE 512
+/* MT7902 Rx Ring0 is for both Rx Event and Tx Done Event */
+#define MT7902_RX_MCU_RING_SIZE 512
+
#define MT7921_EEPROM_SIZE 3584
#define MT7921_TOKEN_SIZE 8192
@@ -117,6 +122,17 @@ enum mt7921_rxq_id {
MT7921_RXQ_MCU_WM = 0,
};
+/* MT7902 assigns its MCU-WM TXQ at index 15 */
+enum mt7902_txq_id {
+ MT7902_TXQ_MCU_WM = 15,
+};
+
+struct mt7921_dma_layout {
+ u8 mcu_wm_txq;
+ u16 mcu_rxdone_ring_size;
+ bool has_mcu_wa;
+};
+
enum {
MT7921_CLC_POWER,
MT7921_CLC_CHAN,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index ec9686183251..7a790ddf43bb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -26,6 +26,8 @@ static const struct pci_device_id mt7921_pci_device_table[] = {
.driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
{ PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7920),
.driver_data = (kernel_ulong_t)MT7920_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7902),
+ .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM },
{ },
};
@@ -167,8 +169,29 @@ static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
static int mt7921_dma_init(struct mt792x_dev *dev)
{
+ struct mt7921_dma_layout layout = {
+ /* General case: MT7921 / MT7922 /MT7920 */
+ .mcu_wm_txq = MT7921_TXQ_MCU_WM,
+ .mcu_rxdone_ring_size = MT7921_RX_MCU_RING_SIZE,
+ .has_mcu_wa = true,
+ };
+ bool is_mt7902;
int ret;
+ is_mt7902 = mt7921_l1_rr(dev, MT_HW_CHIPID) == 0x7902;
+
+ /*
+ * MT7902 special case:
+ * - MCU-WM TXQ uses index 15
+ * - RX Ring0 is larger and shared for event/TX-done
+ * - MT7902 does not use the MCU_WA ring
+ */
+ if (is_mt7902) {
+ layout.mcu_wm_txq = MT7902_TXQ_MCU_WM;
+ layout.mcu_rxdone_ring_size = MT7902_RX_MCU_RING_SIZE;
+ layout.has_mcu_wa = false;
+ }
+
mt76_dma_attach(&dev->mt76);
ret = mt792x_dma_disable(dev, true);
@@ -185,7 +208,7 @@ static int mt7921_dma_init(struct mt792x_dev *dev)
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4);
/* command to WM */
- ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7921_TXQ_MCU_WM,
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, layout.mcu_wm_txq,
MT7921_TX_MCU_RING_SIZE, MT_TX_RING_BASE);
if (ret)
return ret;
@@ -199,18 +222,20 @@ static int mt7921_dma_init(struct mt792x_dev *dev)
/* event from WM before firmware download */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
MT7921_RXQ_MCU_WM,
- MT7921_RX_MCU_RING_SIZE,
+ layout.mcu_rxdone_ring_size,
MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE);
if (ret)
return ret;
- /* Change mcu queue after firmware download */
- ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
- MT7921_RXQ_MCU_WM,
- MT7921_RX_MCU_WA_RING_SIZE,
- MT_RX_BUF_SIZE, MT_WFDMA0(0x540));
- if (ret)
- return ret;
+ if (layout.has_mcu_wa) {
+ /* Change mcu queue after firmware download */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA],
+ MT7921_RXQ_MCU_WM,
+ MT7921_RX_MCU_WA_RING_SIZE,
+ MT_RX_BUF_SIZE, MT_WFDMA0(0x540));
+ if (ret)
+ return ret;
+ }
/* rx data */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
@@ -276,6 +301,7 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
struct mt76_bus_ops *bus_ops;
struct mt792x_dev *dev;
struct mt76_dev *mdev;
+ void __iomem *regs;
u16 cmd, chipid;
u8 features;
int ret;
@@ -284,10 +310,6 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
- ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
- if (ret)
- return ret;
-
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
if (!(cmd & PCI_COMMAND_MEMORY)) {
cmd |= PCI_COMMAND_MEMORY;
@@ -321,11 +343,29 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mdev);
+ regs = pcim_iomap_region(pdev, 0, pci_name(pdev));
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &mt7921_pcie_ops;
dev->irq_map = &irq_map;
- mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
+ mt76_mmio_init(&dev->mt76, regs);
+
+ if (id->device == 0x7902) {
+ struct mt792x_irq_map *map;
+
+ /* MT7902 needs a mutable copy because wm2_complete_mask differs */
+ map = devm_kmemdup(&pdev->dev, &irq_map,
+ sizeof(irq_map), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ map->rx.wm2_complete_mask = 0;
+ dev->irq_map = map;
+ }
+
tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev);
dev->phy.dev = dev;
@@ -579,6 +619,8 @@ MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
MODULE_FIRMWARE(MT7922_FIRMWARE_WM);
MODULE_FIRMWARE(MT7922_ROM_PATCH);
+MODULE_FIRMWARE(MT7902_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7902_ROM_PATCH);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("MediaTek MT7921E (PCIe) wireless driver");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
index 5ec084432ae3..0db7acb3a637 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
@@ -71,9 +71,9 @@ int mt7921e_mac_reset(struct mt792x_dev *dev)
mt76_txq_schedule_all(&dev->mphy);
mt76_worker_disable(&dev->mt76.tx_worker);
- napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]);
- napi_disable(&dev->mt76.napi[MT_RXQ_MCU]);
- napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]);
+ mt76_for_each_q_rx(&dev->mt76, i) {
+ napi_disable(&dev->mt76.napi[i]);
+ }
napi_disable(&dev->mt76.tx_napi);
mt76_connac2_tx_token_put(&dev->mt76);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
index 3421e53dc948..9150f185716c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
@@ -19,6 +19,8 @@
static const struct sdio_device_id mt7921s_table[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901),
.driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7902),
+ .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM },
{ } /* Terminating entry */
};
@@ -317,6 +319,8 @@ failed:
MODULE_DEVICE_TABLE(sdio, mt7921s_table);
MODULE_FIRMWARE(MT7921_FIRMWARE_WM);
MODULE_FIRMWARE(MT7921_ROM_PATCH);
+MODULE_FIRMWARE(MT7902_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7902_ROM_PATCH);
static DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index 3ce5d6fcc69d..c0c5cb9aff75 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -91,6 +91,8 @@ int mt7925_mac_init(struct mt792x_dev *dev)
mt7925_mac_init_basic_rates(dev);
+ memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2));
+
return 0;
}
EXPORT_SYMBOL_GPL(mt7925_mac_init);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index caaf71c31480..c47bd812b66b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -804,8 +804,8 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
txwi[5] = cpu_to_le32(val);
val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
- if (!ieee80211_vif_is_mld(vif) ||
- (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))
+ if (vif && (!ieee80211_vif_is_mld(vif) ||
+ (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0)))
val |= MT_TXD6_DIS_MAT;
txwi[6] = cpu_to_le32(val);
txwi[7] = 0;
@@ -846,11 +846,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
bool is_8023;
u16 fc, tid;
+ if (!sta)
+ return;
+
link_sta = rcu_dereference(sta->link[wcid->link_id]);
if (!link_sta)
return;
- if (!sta || !(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
+ if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
return;
tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
@@ -882,8 +885,10 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
else
mlink = &msta->deflink;
- if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state))
- ieee80211_start_tx_ba_session(sta, tid, 0);
+ if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) {
+ if (ieee80211_start_tx_ba_session(sta, tid, 0))
+ clear_bit(tid, &mlink->wcid.ampdu_state);
+ }
}
static bool
@@ -1280,7 +1285,8 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
if (vif->type == NL80211_IFTYPE_AP) {
mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.deflink.wcid,
true, NULL);
- mt7925_mcu_sta_update(dev, NULL, vif, true,
+ mt7925_mcu_sta_update(dev, NULL, vif,
+ &mvif->sta.deflink, true,
MT76_STA_INFO_STATE_NONE);
mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 2d358a96640c..73d3722739d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -245,6 +245,7 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy)
{
struct wiphy *wiphy = phy->mt76->hw->wiphy;
static const u8 ext_capa_sta[] = {
+ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF,
};
@@ -438,6 +439,9 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN)
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+ INIT_WORK(&mvif->csa_work, mt7925_csa_work);
+ timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0);
+
out:
mt792x_mutex_release(dev);
@@ -457,12 +461,16 @@ void mt7925_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
+ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ return;
+
timer_delete_sync(&phy->roc_timer);
- cancel_work_sync(&phy->roc_work);
- if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
- ieee80211_iterate_interfaces(mt76_hw(dev),
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7925_roc_iter, (void *)phy);
+
+ cancel_work(&phy->roc_work);
+
+ ieee80211_iterate_interfaces(mt76_hw(dev),
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_roc_iter, (void *)phy);
}
EXPORT_SYMBOL_GPL(mt7925_roc_abort_sync);
@@ -541,7 +549,7 @@ static int mt7925_set_mlo_roc(struct mt792x_phy *phy,
phy->roc_grant = false;
- err = mt7925_mcu_set_mlo_roc(mconf, sel_links, 5, ++phy->roc_token_id);
+ err = mt7925_mcu_set_mlo_roc(phy, mconf, sel_links, 5, ++phy->roc_token_id);
if (err < 0) {
clear_bit(MT76_STATE_ROC, &phy->mt76->state);
goto out;
@@ -586,7 +594,8 @@ static int mt7925_cancel_remain_on_channel(struct ieee80211_hw *hw,
static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key, int link_id)
+ struct ieee80211_key_conf *key, int link_id,
+ struct mt792x_link_sta *mlink)
{
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
@@ -595,7 +604,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
int idx = key->keyidx, err = 0;
- struct mt792x_link_sta *mlink;
struct mt792x_bss_conf *mconf;
struct mt76_wcid *wcid;
u8 *wcid_keyidx;
@@ -603,7 +611,6 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL;
mconf = mt792x_vif_to_link(mvif, link_id);
- mlink = mt792x_sta_to_link(msta, link_id);
wcid = &mlink->wcid;
wcid_keyidx = &wcid->hw_key_idx;
@@ -671,6 +678,7 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv :
&mvif->sta;
+ struct mt792x_link_sta *mlink;
int err;
/* The hardware does not support per-STA RX GTK, fallback
@@ -692,12 +700,16 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
add = key->link_id != -1 ? BIT(key->link_id) : msta->valid_links;
for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
- err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id);
+ mlink = mt792x_sta_to_link(msta, link_id);
+ err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id,
+ mlink);
if (err < 0)
break;
}
} else {
- err = mt7925_set_link_key(hw, cmd, vif, sta, key, vif->bss_conf.link_id);
+ mlink = mt792x_sta_to_link(msta, vif->bss_conf.link_id);
+ err = mt7925_set_link_key(hw, cmd, vif, sta, key,
+ vif->bss_conf.link_id, mlink);
}
mt792x_mutex_release(dev);
@@ -842,20 +854,24 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta)
+ struct ieee80211_link_sta *link_sta,
+ struct mt792x_link_sta *mlink)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct ieee80211_bss_conf *link_conf;
struct mt792x_bss_conf *mconf;
u8 link_id = link_sta->link_id;
- struct mt792x_link_sta *mlink;
+ bool wcid_published = false;
struct mt792x_sta *msta;
struct mt76_wcid *wcid;
+ bool pm_woken = false;
int ret, idx;
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- mlink = mt792x_sta_to_link(msta, link_id);
+
+ if (WARN_ON_ONCE(!mlink))
+ return -EINVAL;
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
if (idx < 0)
@@ -874,14 +890,15 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
wcid = &mlink->wcid;
ewma_signal_init(&wcid->rssi);
rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid);
- mt76_wcid_init(wcid, 0);
+ wcid_published = true;
ewma_avg_signal_init(&mlink->avg_ack_signal);
memset(mlink->airtime_ac, 0,
sizeof(msta->deflink.airtime_ac));
ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
if (ret)
- return ret;
+ goto out_wcid;
+ pm_woken = true;
mt7925_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -890,41 +907,125 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
/* should update bss info before STA add */
if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
- if (ieee80211_vif_is_mld(vif))
- mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta, link_sta != mlink->pri_link);
- else
- mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta, false);
+ struct mt792x_link_sta *mlink_bc;
+
+ mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id);
+
+ if (ieee80211_vif_is_mld(vif)) {
+ ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta,
+ mlink_bc->wcid.idx, mlink->wcid.idx,
+ link_sta != mlink->pri_link);
+ if (ret)
+ goto out_pm;
+ } else {
+ ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta,
+ mlink_bc->wcid.idx, mlink->wcid.idx,
+ false);
+ if (ret)
+ goto out_pm;
+ }
}
if (ieee80211_vif_is_mld(vif) &&
link_sta == mlink->pri_link) {
- ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
+ ret = mt7925_mcu_sta_update(dev, link_sta, vif,
+ mlink, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto out_pm;
} else if (ieee80211_vif_is_mld(vif) &&
link_sta != mlink->pri_link) {
+ struct mt792x_link_sta *pri_mlink;
+ struct mt76_wcid *pri_wcid;
+
+ /* alternative lookup via def_wcid */
+ pri_wcid = mlink->wcid.def_wcid;
+
+ pri_mlink = pri_wcid ?
+ container_of(pri_wcid, struct mt792x_link_sta, wcid) :
+ NULL;
+
+ if (WARN_ON_ONCE(!pri_mlink)) {
+ ret = -EINVAL;
+ goto out_pm;
+ }
+
ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
- true, MT76_STA_INFO_STATE_ASSOC);
+ pri_mlink, true,
+ MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto out_pm;
- ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
+ ret = mt7925_mcu_sta_update(dev, link_sta, vif,
+ mlink, true,
MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto out_pm;
} else {
- ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
+ ret = mt7925_mcu_sta_update(dev, link_sta, vif,
+ mlink, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto out_pm;
}
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
return 0;
+
+out_pm:
+ if (pm_woken)
+ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+out_wcid:
+ if (wcid_published) {
+ u16 idx = wcid->idx;
+
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(mdev, wcid);
+ mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
+ }
+ return ret;
+}
+
+/*
+ * Host-only unwind for sta_add_links() failures.
+ *
+ * If add_links fail due to MCU/firmware timeouts; calling the full remove
+ * path would send more firmware commands and may hang again. So only rollback
+ * host-published state here (msta->link/valid_links, dev->mt76.wcid[idx]) and
+ * free mlink objects (RCU-safe). Firmware state is left for reset/recovery.
+ */
+static void
+mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev,
+ struct ieee80211_sta *sta,
+ unsigned long links)
+{
+ struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_link_sta *mlink;
+ u16 idx;
+
+ mlink = rcu_replace_pointer(msta->link[link_id], NULL,
+ lockdep_is_held(&dev->mt76.mutex));
+ if (!mlink)
+ continue;
+
+ msta->valid_links &= ~BIT(link_id);
+ if (msta->deflink_id == link_id)
+ msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+
+ idx = mlink->wcid.idx;
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+
+ if (mlink != &msta->deflink)
+ kfree_rcu(mlink, rcu_head);
+ }
}
static int
@@ -932,34 +1033,50 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long new_links)
{
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+ unsigned long added_links = 0;
unsigned int link_id;
int err = 0;
for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_link_sta *link_sta;
struct mt792x_link_sta *mlink;
+ bool is_deflink = false;
if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) {
mlink = &msta->deflink;
- msta->deflink_id = link_id;
+ is_deflink = true;
} else {
- mlink = devm_kzalloc(dev->mt76.dev, sizeof(*mlink), GFP_KERNEL);
+ mlink = kzalloc(sizeof(*mlink), GFP_KERNEL);
if (!mlink) {
err = -ENOMEM;
break;
}
}
- msta->valid_links |= BIT(link_id);
- rcu_assign_pointer(msta->link[link_id], mlink);
mlink->sta = msta;
mlink->pri_link = &sta->deflink;
mlink->wcid.def_wcid = &msta->deflink.wcid;
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
- mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta);
+ err = mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta, mlink);
+ if (err) {
+ if (!is_deflink)
+ kfree_rcu(mlink, rcu_head);
+ break;
+ }
+
+ if (is_deflink)
+ msta->deflink_id = link_id;
+
+ rcu_assign_pointer(msta->link[link_id], mlink);
+ msta->valid_links |= BIT(link_id);
+
+ added_links |= BIT(link_id);
}
+ if (err && added_links)
+ mt7925_mac_sta_unwind_links_host(dev, sta, added_links);
+
return err;
}
@@ -981,7 +1098,8 @@ int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
err = mt7925_mac_sta_add_links(dev, vif, sta, sta->valid_links);
} else {
- err = mt7925_mac_link_sta_add(mdev, vif, &sta->deflink);
+ err = mt7925_mac_link_sta_add(mdev, vif, &sta->deflink,
+ &msta->deflink);
}
return err;
@@ -1030,11 +1148,11 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
struct mt792x_link_sta *mlink;
struct mt792x_sta *msta;
+ mt792x_mutex_acquire(dev);
+
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
- mt792x_mutex_acquire(dev);
-
if (ieee80211_vif_is_mld(vif)) {
link_conf = mt792x_vif_to_bss_conf(vif, msta->deflink_id);
} else {
@@ -1055,7 +1173,8 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac));
- mt7925_mcu_sta_update(dev, link_sta, vif, true, MT76_STA_INFO_STATE_ASSOC);
+ mt7925_mcu_sta_update(dev, link_sta, vif, mlink, true,
+ MT76_STA_INFO_STATE_ASSOC);
mt792x_mutex_release(dev);
}
@@ -1083,23 +1202,21 @@ EXPORT_SYMBOL_GPL(mt7925_mac_sta_event);
static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta)
+ struct ieee80211_link_sta *link_sta,
+ struct mt792x_link_sta *mlink)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
+ struct mt76_wcid *wcid = &mlink->wcid;
struct ieee80211_bss_conf *link_conf;
u8 link_id = link_sta->link_id;
- struct mt792x_link_sta *mlink;
- struct mt792x_sta *msta;
-
- msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- mlink = mt792x_sta_to_link(msta, link_id);
+ u16 idx = wcid->idx;
mt7925_roc_abort_sync(dev);
- mt76_connac_free_pending_tx_skbs(&dev->pm, &mlink->wcid);
+ mt76_connac_free_pending_tx_skbs(&dev->pm, wcid);
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
- mt7925_mcu_sta_update(dev, link_sta, vif, false,
+ mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false,
MT76_STA_INFO_STATE_NONE);
mt7925_mac_wtbl_update(dev, mlink->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -1123,6 +1240,10 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
list_del_init(&mlink->wcid.poll_list);
spin_unlock_bh(&mdev->sta_poll_lock);
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_cleanup(mdev, wcid);
+ mt76_wcid_mask_clear(mdev->wcid_mask, idx);
+
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
}
@@ -1132,7 +1253,6 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
{
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
struct mt76_dev *mdev = &dev->mt76;
- struct mt76_wcid *wcid;
unsigned int link_id;
/* clean up bss before starec */
@@ -1171,22 +1291,19 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
if (!link_sta)
continue;
- mlink = mt792x_sta_to_link(msta, link_id);
+ mlink = rcu_replace_pointer(msta->link[link_id], NULL,
+ lockdep_is_held(&mdev->mutex));
if (!mlink)
continue;
- mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta);
-
- wcid = &mlink->wcid;
- rcu_assign_pointer(msta->link[link_id], NULL);
msta->valid_links &= ~BIT(link_id);
mlink->sta = NULL;
mlink->pri_link = NULL;
- if (link_sta != mlink->pri_link) {
- mt76_wcid_cleanup(mdev, wcid);
- mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx);
- }
+ mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink);
+
+ if (mlink != &msta->deflink)
+ kfree_rcu(mlink, rcu_head);
if (msta->deflink_id == link_id)
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
@@ -1323,10 +1440,18 @@ void mt7925_mlo_pm_work(struct work_struct *work)
void mt7925_scan_work(struct work_struct *work)
{
struct mt792x_phy *phy;
+ struct mt792x_dev *dev;
+ struct mt76_connac_pm *pm;
phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
scan_work.work);
+ dev = phy->dev;
+ pm = &dev->pm;
+
+ if (pm->suspended)
+ return;
+
while (true) {
struct sk_buff *skb;
struct tlv *tlv;
@@ -1544,8 +1669,10 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
valid = ieee80211_vif_is_mld(vif) ? mvif->valid_links : BIT(0);
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_bss_conf *mconf;
struct mt792x_link_sta *mlink;
+ mconf = mt792x_vif_to_link(mvif, i);
mlink = mt792x_sta_to_link(msta, i);
if (enabled)
@@ -1556,7 +1683,7 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
if (!mlink->wcid.sta)
continue;
- mt7925_mcu_wtbl_update_hdr_trans(dev, vif, sta, i);
+ mt7925_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink);
}
mt792x_mutex_release(dev);
@@ -1716,7 +1843,8 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (err)
goto out;
- err = mt7925_mcu_sta_update(dev, NULL, vif, true,
+ err = mt7925_mcu_sta_update(dev, NULL, vif,
+ &mvif->sta.deflink, true,
MT76_STA_INFO_STATE_NONE);
out:
mt792x_mutex_release(dev);
@@ -1749,6 +1877,10 @@ static int
mt7925_add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ dev->new_ctx = ctx;
+
return 0;
}
@@ -1756,6 +1888,11 @@ static void
mt7925_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ if (dev->new_ctx == ctx)
+ dev->new_ctx = NULL;
+
}
static void
@@ -1850,7 +1987,8 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
mt792x_mutex_acquire(dev);
if (changed & BSS_CHANGED_ASSOC) {
- mt7925_mcu_sta_update(dev, NULL, vif, true,
+ mt7925_mcu_sta_update(dev, NULL, vif,
+ &mvif->sta.deflink, true,
MT76_STA_INFO_STATE_ASSOC);
mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
@@ -1894,10 +2032,8 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
struct mt792x_phy *phy = mt792x_hw_phy(hw);
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt792x_bss_conf *mconf;
- struct ieee80211_bss_conf *link_conf;
mconf = mt792x_vif_to_link(mvif, info->link_id);
- link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id);
mt792x_mutex_acquire(dev);
@@ -1939,10 +2075,6 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS;
}
- if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING)
- mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76,
- link_conf, NULL);
-
if (changed & BSS_CHANGED_CQM)
mt7925_mcu_set_rssimonitor(dev, vif);
@@ -2144,6 +2276,11 @@ static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw,
mctx->bss_conf = NULL;
mconf->mt76.ctx = NULL;
mutex_unlock(&dev->mt76.mutex);
+
+ if (link_conf->csa_active) {
+ timer_delete_sync(&mvif->csa_timer);
+ cancel_work_sync(&mvif->csa_work);
+ }
}
static void mt7925_rfkill_poll(struct ieee80211_hw *hw)
@@ -2158,6 +2295,121 @@ static void mt7925_rfkill_poll(struct ieee80211_hw *hw)
wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0);
}
+static int mt7925_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ return mt7925_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf,
+ vifs->new_ctx);
+}
+
+void mt7925_csa_work(struct work_struct *work)
+{
+ struct mt792x_vif *mvif;
+ struct mt792x_dev *dev;
+ struct ieee80211_vif *vif;
+ struct ieee80211_bss_conf *link_conf;
+ struct mt792x_bss_conf *mconf;
+ u8 link_id, roc_rtype;
+ int ret = 0;
+
+ mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif,
+ csa_work);
+ dev = mvif->phy->dev;
+ vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
+
+ if (ieee80211_vif_is_mld(vif))
+ return;
+
+ if (!dev->new_ctx)
+ return;
+
+ link_id = 0;
+ mconf = &mvif->bss_conf;
+ link_conf = &vif->bss_conf;
+ roc_rtype = MT7925_ROC_REQ_JOIN;
+
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_set_roc(mvif->phy, mconf, dev->new_ctx->def.chan,
+ 4000, roc_rtype);
+ mt792x_mutex_release(dev);
+ if (!ret) {
+ mt792x_mutex_acquire(dev);
+ ret = mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76, link_conf,
+ dev->new_ctx);
+ mt792x_mutex_release(dev);
+
+ mt7925_abort_roc(mvif->phy, mconf);
+ }
+
+ ieee80211_chswitch_done(vif, !ret, link_id);
+}
+
+static int mt7925_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ if (ieee80211_vif_is_mld(vif))
+ return -EOPNOTSUPP;
+
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc)
+ return -EOPNOTSUPP;
+
+ if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static void mt7925_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ u16 beacon_interval;
+
+ if (ieee80211_vif_is_mld(vif))
+ return;
+
+ beacon_interval = vif->bss_conf.beacon_int;
+
+ mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count);
+ add_timer(&mvif->csa_timer);
+}
+
+static void mt7925_abort_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+ timer_delete_sync(&mvif->csa_timer);
+ cancel_work_sync(&mvif->csa_work);
+}
+
+static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ u16 beacon_interval;
+
+ if (ieee80211_vif_is_mld(vif))
+ return;
+
+ beacon_interval = vif->bss_conf.beacon_int;
+
+ if (cfg80211_chandef_identical(&chsw->chandef,
+ &dev->new_ctx->def) &&
+ chsw->count) {
+ mod_timer(&mvif->csa_timer,
+ TU_TO_EXP_TIME(beacon_interval * chsw->count));
+ }
+}
+
const struct ieee80211_ops mt7925_ops = {
.tx = mt792x_tx,
.start = mt7925_start,
@@ -2221,6 +2473,12 @@ const struct ieee80211_ops mt7925_ops = {
.change_vif_links = mt7925_change_vif_links,
.change_sta_links = mt7925_change_sta_links,
.rfkill_poll = mt7925_rfkill_poll,
+
+ .switch_vif_chanctx = mt7925_switch_vif_chanctx,
+ .pre_channel_switch = mt7925_pre_channel_switch,
+ .channel_switch = mt7925_channel_switch,
+ .abort_channel_switch = mt7925_abort_channel_switch,
+ .channel_switch_rx_beacon = mt7925_channel_switch_rx_beacon,
};
EXPORT_SYMBOL_GPL(mt7925_ops);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index cf0fdea45cf7..37cdf3e8a067 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1066,9 +1066,8 @@ EXPORT_SYMBOL_GPL(mt7925_run_firmware);
static void
mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta)
+ struct mt792x_link_sta *mlink)
{
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct sta_rec_hdr_trans *hdr_trans;
struct mt76_wcid *wcid;
struct tlv *tlv;
@@ -1082,19 +1081,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
else
hdr_trans->from_ds = true;
- if (link_sta) {
- struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- struct mt792x_link_sta *mlink;
-
- mlink = mt792x_sta_to_link(msta, link_sta->link_id);
- wcid = &mlink->wcid;
- } else {
- wcid = &mvif->sta.deflink.wcid;
- }
-
- if (!wcid)
+ if (WARN_ON_ONCE(!mlink))
return;
+ wcid = &mlink->wcid;
+
hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
hdr_trans->to_ds = true;
@@ -1104,30 +1095,18 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- int link_id)
+ struct mt792x_bss_conf *mconf,
+ struct mt792x_link_sta *mlink)
{
- struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct ieee80211_link_sta *link_sta = sta ? &sta->deflink : NULL;
- struct mt792x_link_sta *mlink;
- struct mt792x_bss_conf *mconf;
- struct mt792x_sta *msta;
struct sk_buff *skb;
- msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta;
-
- mlink = mt792x_sta_to_link(msta, link_id);
- link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
- mconf = mt792x_vif_to_link(mvif, link_id);
-
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
&mlink->wcid,
MT7925_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
- /* starec hdr trans */
- mt7925_mcu_sta_hdr_trans_tlv(skb, vif, link_sta);
+ mt7925_mcu_sta_hdr_trans_tlv(skb, vif, mlink);
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
@@ -1288,14 +1267,16 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
return PTR_ERR(skb);
ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd, msta);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
-int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
- int duration, u8 token_id)
+int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
+ u16 sel_links, int duration, u8 token_id)
{
struct mt792x_vif *mvif = mconf->vif;
struct ieee80211_vif *vif = container_of((void *)mvif,
@@ -1330,6 +1311,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
.roc[1].len = cpu_to_le16(sizeof(struct roc_acquire_tlv))
};
+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
+
if (!mconf || hweight16(vif->valid_links) < 2 ||
hweight16(sel_links) != 2)
return -EPERM;
@@ -1352,7 +1335,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
is_AG_band |= links[i].chan->band == NL80211_BAND_2GHZ;
}
- if (vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP)
+ if (!(wiphy->iftype_ext_capab[0].mld_capa_and_ops &
+ IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS))
type = is_AG_band ? MT7925_ROC_REQ_MLSR_AG :
MT7925_ROC_REQ_MLSR_AA;
else
@@ -1721,10 +1705,9 @@ mt7925_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
static void
mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif,
- struct ieee80211_link_sta *link_sta)
+ struct ieee80211_link_sta *link_sta,
+ struct mt792x_link_sta *mlink)
{
- struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- struct mt792x_link_sta *mlink;
struct sta_rec_amsdu *amsdu;
struct tlv *tlv;
@@ -1740,7 +1723,6 @@ mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb,
amsdu->max_amsdu_num = 8;
amsdu->amsdu_en = true;
- mlink = mt792x_sta_to_link(msta, link_sta->link_id);
mlink->wcid.amsdu = true;
switch (link_sta->agg.max_amsdu_len) {
@@ -1911,36 +1893,53 @@ mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb,
static void
mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
- struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct mt792x_bss_conf *mconf,
+ struct mt792x_link_sta *mlink)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
- unsigned long valid = mvif->valid_links;
- struct mt792x_bss_conf *mconf;
- struct mt792x_link_sta *mlink;
+ struct mt792x_dev *dev = mvif->phy->dev;
+ struct mt792x_bss_conf *mconf_pri;
struct sta_rec_mld *mld;
struct tlv *tlv;
- int i, cnt = 0;
+ u8 cnt = 0;
+
+ /* Primary link always uses driver's deflink WCID. */
+ mconf_pri = (msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) ?
+ mt792x_vif_to_link(mvif, msta->deflink_id) : NULL;
+
+ /* If caller is operating on deflink, reuse its mconf as primary. */
+ if (!mconf_pri && mlink == &msta->deflink)
+ mconf_pri = mconf;
+
+ if (!mconf_pri) {
+ dev_warn_ratelimited(dev->mt76.dev,
+ "mt7925: MLD_TLV_LINK skip (no primary mconf) sta=%pM\n",
+ sta->addr);
+ return;
+ }
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(*mld));
mld = (struct sta_rec_mld *)tlv;
memcpy(mld->mac_addr, sta->addr, ETH_ALEN);
+
mld->primary_id = cpu_to_le16(msta->deflink.wcid.idx);
mld->wlan_id = cpu_to_le16(msta->deflink.wcid.idx);
- mld->link_num = min_t(u8, hweight16(mvif->valid_links), 2);
- for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
- if (cnt == mld->link_num)
- break;
+ /* Always encode primary link first. */
+ mld->link[cnt].wlan_id = cpu_to_le16(msta->deflink.wcid.idx);
+ mld->link[cnt++].bss_idx = mconf_pri->mt76.idx;
- mconf = mt792x_vif_to_link(mvif, i);
- mlink = mt792x_sta_to_link(msta, i);
+ /* Optionally encode the currently-updated secondary link. */
+ if (mlink && mlink != &msta->deflink && mconf) {
+ mld->secondary_id = cpu_to_le16(mlink->wcid.idx);
mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx);
mld->link[cnt++].bss_idx = mconf->mt76.idx;
-
- if (mlink != &msta->deflink)
- mld->secondary_id = cpu_to_le16(mlink->wcid.idx);
}
+
+ mld->link_num = cnt;
}
static void
@@ -1961,10 +1960,12 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv;
struct mt76_dev *dev = phy->dev;
struct mt792x_bss_conf *mconf;
+ struct mt792x_link_sta *mlink;
struct sk_buff *skb;
int conn_state;
mconf = mt792x_vif_to_link(mvif, info->wcid->link_id);
+ mlink = container_of(info->wcid, struct mt792x_link_sta, wcid);
skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, info->wcid,
MT7925_STA_UPDATE_MAX_SIZE);
@@ -1982,7 +1983,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
mt7925_mcu_sta_ht_tlv(skb, info->link_sta);
mt7925_mcu_sta_vht_tlv(skb, info->link_sta);
mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta);
- mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta);
+ mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta, mlink);
mt7925_mcu_sta_he_tlv(skb, info->link_sta);
mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta);
mt7925_mcu_sta_eht_tlv(skb, info->link_sta);
@@ -1993,7 +1994,10 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
info->state);
if (info->state != MT76_STA_INFO_STATE_NONE) {
- mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta);
+ mt7925_mcu_sta_mld_tlv(skb, info->vif,
+ info->link_sta->sta,
+ mconf, mlink);
+
mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta);
}
}
@@ -2003,7 +2007,10 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF,
sizeof(struct tlv));
} else {
- mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta);
+ if (!info->link_sta)
+ mlink = &mvif->sta.deflink;
+
+ mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, mlink);
}
return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true);
@@ -2011,7 +2018,9 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_link_sta *link_sta,
- struct ieee80211_vif *vif, bool enable,
+ struct ieee80211_vif *vif,
+ struct mt792x_link_sta *mlink,
+ bool enable,
enum mt76_sta_info_state state)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
@@ -2026,14 +2035,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
.offload_fw = true,
.rcpi = to_rcpi(rssi),
};
- struct mt792x_sta *msta;
- struct mt792x_link_sta *mlink;
- if (link_sta) {
- msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- mlink = mt792x_sta_to_link(msta, link_sta->link_id);
- }
- info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid;
+ info.wcid = &mlink->wcid;
info.newly = state != MT76_STA_INFO_STATE_ASSOC;
return mt7925_mcu_sta_cmd(&dev->mphy, &info);
@@ -2470,7 +2473,9 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
struct ieee80211_chanctx_conf *ctx,
- struct mt76_phy *phy, u16 wlan_idx,
+ struct mt76_phy *phy,
+ u16 bmc_tx_wlan_idx,
+ u16 sta_wlan_idx,
bool enable)
{
struct ieee80211_vif *vif = link_conf->vif;
@@ -2479,7 +2484,6 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
&link_conf->chanreq.oper;
enum nl80211_band band = chandef->chan->band;
struct mt76_connac_bss_basic_tlv *basic_req;
- struct mt792x_link_sta *mlink;
struct tlv *tlv;
int conn_type;
u8 idx;
@@ -2503,20 +2507,9 @@ mt7925_mcu_bss_basic_tlv(struct sk_buff *skb,
basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, link_sta);
basic_req->bcn_interval = cpu_to_le16(link_conf->beacon_int);
basic_req->dtim_period = link_conf->dtim_period;
- basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+ basic_req->bmc_tx_wlan_idx = cpu_to_le16(bmc_tx_wlan_idx);
basic_req->link_idx = mconf->mt76.idx;
-
- if (link_sta) {
- struct mt792x_sta *msta;
-
- msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
- mlink = mt792x_sta_to_link(msta, link_sta->link_id);
-
- } else {
- mlink = &mconf->vif->sta.deflink;
- }
-
- basic_req->sta_idx = cpu_to_le16(mlink->wcid.idx);
+ basic_req->sta_idx = cpu_to_le16(sta_wlan_idx);
basic_req->omac_idx = mconf->mt76.omac_idx;
basic_req->band_idx = mconf->mt76.band_idx;
basic_req->wmm_idx = mconf->mt76.wmm_idx;
@@ -2823,16 +2816,16 @@ void mt7925_mcu_del_dev(struct mt76_dev *mdev,
&dev_req, sizeof(dev_req), true);
}
-int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
- struct ieee80211_chanctx_conf *ctx,
- struct ieee80211_bss_conf *link_conf,
- struct ieee80211_link_sta *link_sta,
- int enable)
+int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy,
+ struct ieee80211_chanctx_conf *ctx,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ u16 bmc_tx_wlan_idx,
+ u16 sta_wlan_idx,
+ int enable)
{
- struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv;
struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf);
struct mt792x_dev *dev = phy->dev;
- struct mt792x_link_sta *mlink_bc;
struct sk_buff *skb;
skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
@@ -2840,11 +2833,9 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
if (IS_ERR(skb))
return PTR_ERR(skb);
- mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id);
-
/* bss_basic must be first */
mt7925_mcu_bss_basic_tlv(skb, link_conf, link_sta, ctx, phy->mt76,
- mlink_bc->wcid.idx, enable);
+ bmc_tx_wlan_idx, sta_wlan_idx, enable);
mt7925_mcu_bss_sec_tlv(skb, link_conf);
mt7925_mcu_bss_bmc_tlv(skb, phy, ctx, link_conf);
mt7925_mcu_bss_qos_tlv(skb, link_conf);
@@ -2865,6 +2856,33 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
MCU_UNI_CMD(BSS_INFO_UPDATE), true);
}
+int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
+ struct ieee80211_chanctx_conf *ctx,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ int enable)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv;
+ struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf);
+ struct mt792x_link_sta *mlink_bc;
+ struct mt792x_link_sta *mlink;
+
+ mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id);
+
+ if (link_sta) {
+ struct mt792x_sta *msta = (void *)link_sta->sta->drv_priv;
+
+ mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+ if (WARN_ON(!mlink))
+ return -EINVAL;
+ } else {
+ mlink = &mconf->vif->sta.deflink;
+ }
+
+ return mt7925_mcu_add_bss_info_sta(phy, ctx, link_conf, link_sta,
+ mlink_bc->wcid.idx, mlink->wcid.idx, enable);
+}
+
int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable)
{
struct mt76_dev *mdev = phy->dev;
@@ -3375,7 +3393,6 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
u8 rsvd[64];
} __packed req = {
.tag = cpu_to_le16(0x3),
- .len = cpu_to_le16(sizeof(req) - 4),
.idx = idx,
.env = env_cap,
@@ -3404,6 +3421,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
memcpy(req.type, rule->type, 2);
req.size = cpu_to_le16(seg->len);
+ req.len = cpu_to_le16(sizeof(req) + seg->len - 4);
dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag;
skb = __mt76_mcu_msg_alloc(&dev->mt76, &req,
le16_to_cpu(req.size) + sizeof(req),
@@ -3727,7 +3745,7 @@ mt7925_mcu_rate_txpower_band(struct mt76_phy *phy,
memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2));
tx_power_tlv->n_chan = num_ch;
tx_power_tlv->tag = cpu_to_le16(0x1);
- tx_power_tlv->len = cpu_to_le16(sizeof(*tx_power_tlv));
+ tx_power_tlv->len = cpu_to_le16(msg_len);
switch (band) {
case NL80211_BAND_2GHZ:
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index e09e0600534a..56e2772f3ffe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -693,6 +693,13 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
int enable);
+int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy,
+ struct ieee80211_chanctx_conf *ctx,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_link_sta *link_sta,
+ u16 bmc_tx_wlan_idx,
+ u16 sta_wlan_idx,
+ int enable);
int mt7925_mcu_set_timing(struct mt792x_phy *phy,
struct ieee80211_bss_conf *link_conf);
int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 6b9bf1b89032..46b480f7d813 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -250,7 +250,9 @@ int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev,
bool enable);
int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_link_sta *link_sta,
- struct ieee80211_vif *vif, bool enable,
+ struct ieee80211_vif *vif,
+ struct mt792x_link_sta *mlink,
+ bool enable,
enum mt76_sta_info_state state);
int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag);
int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf);
@@ -298,6 +300,7 @@ int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
void mt7925_mlo_pm_work(struct work_struct *work);
void mt7925_scan_work(struct work_struct *work);
void mt7925_roc_work(struct work_struct *work);
+void mt7925_csa_work(struct work_struct *work);
int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev,
struct ieee80211_bss_conf *link_conf);
void mt7925_coredump_work(struct work_struct *work);
@@ -349,8 +352,8 @@ int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw,
int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set);
int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap env_cap);
-int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
- int duration, u8 token_id);
+int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
+ u16 sel_links, int duration, u8 token_id);
int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf,
struct ieee80211_channel *chan, int duration,
enum mt7925_roc_req type, u8 token_id);
@@ -367,8 +370,8 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val);
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
- struct ieee80211_sta *sta,
- int link_id);
+ struct mt792x_bss_conf *mconf,
+ struct mt792x_link_sta *mlink);
int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy);
int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
index 292087e882d1..16f56ee879d4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
@@ -232,7 +232,8 @@ int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
dev->regd_user)
return -EINVAL;
- if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
+ if ((mdev->alpha2[0] && mdev->alpha2[0] != '0') &&
+ (mdev->alpha2[1] && mdev->alpha2[1] != '0'))
return 0;
/* do not need to update the same country twice */
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 8388638ed550..4ff93f2cd624 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -41,11 +41,13 @@
#define MT792x_MCU_INIT_RETRY_COUNT 10
#define MT792x_WFSYS_INIT_RETRY_COUNT 2
+#define MT7902_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7902_1.bin"
#define MT7920_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1a.bin"
#define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin"
#define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin"
#define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin"
+#define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin"
#define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin"
#define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin"
#define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin"
@@ -95,6 +97,7 @@ DECLARE_EWMA(avg_signal, 10, 8)
struct mt792x_link_sta {
struct mt76_wcid wcid; /* must be first */
+ struct rcu_head rcu_head;
u32 airtime_ac[8];
@@ -448,6 +451,8 @@ void mt792x_config_mac_addr_list(struct mt792x_dev *dev);
static inline char *mt792x_ram_name(struct mt792x_dev *dev)
{
switch (mt76_chip(&dev->mt76)) {
+ case 0x7902:
+ return MT7902_FIRMWARE_WM;
case 0x7920:
return MT7920_FIRMWARE_WM;
case 0x7922:
@@ -462,6 +467,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev)
static inline char *mt792x_patch_name(struct mt792x_dev *dev)
{
switch (mt76_chip(&dev->mt76)) {
+ case 0x7902:
+ return MT7902_ROM_PATCH;
case 0x7920:
return MT7920_ROM_PATCH;
case 0x7922:
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index f2ed16feb6c1..152cfcca2f90 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -151,7 +151,7 @@ void mt792x_stop(struct ieee80211_hw *hw, bool suspend)
cancel_work_sync(&dev->reset_work);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
- if (is_mt7921(&dev->mt76)) {
+ if (is_connac2(&dev->mt76)) {
mt792x_mutex_acquire(dev);
mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false);
mt792x_mutex_release(dev);
@@ -691,9 +691,8 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
- if (is_mt7921(&dev->mt76)) {
- ieee80211_hw_set(hw, CHANCTX_STA_CSA);
- }
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+
if (dev->pm.enable)
ieee80211_hw_set(hw, CONNECTION_MONITOR);
@@ -927,6 +926,13 @@ int mt792x_load_firmware(struct mt792x_dev *dev)
{
int ret;
+ mt76_connac_mcu_restart(&dev->mt76);
+
+ if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC_FW_STATE,
+ MT_TOP_MISC2_FW_PWR_ON, 1000))
+ dev_warn(dev->mt76.dev,
+ "MCU is not ready for firmware download\n");
+
ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev));
if (ret)
return ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
index 1ddec7788b66..002aece857b2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
@@ -103,6 +103,22 @@ static void mt792x_dma_prefetch(struct mt792x_dev *dev)
mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10));
mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4));
+ } else if (is_mt7902(&dev->mt76)) {
+ /* rx ring */
+ mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4));
+ mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4));
+ mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4));
+ mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4));
+ /* tx ring */
+ mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0140, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0180, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x01c0, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0200, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0240, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x0280, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x02c0, 0x4));
+ mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0300, 0x4));
} else {
/* rx ring */
mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4));
@@ -356,7 +372,7 @@ EXPORT_SYMBOL_GPL(mt792x_poll_rx);
int mt792x_wfsys_reset(struct mt792x_dev *dev)
{
- u32 addr = is_mt7921(&dev->mt76) ? 0x18000140 : 0x7c000140;
+ u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140;
mt76_clear(dev, addr, WFSYS_SW_RST_B);
msleep(50);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
index 71dec93094eb..888e5a505673 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
@@ -375,7 +375,7 @@ void mt792x_pm_power_save_work(struct work_struct *work)
}
if (!mt792x_mcu_fw_pmctrl(dev)) {
- cancel_delayed_work_sync(&mphy->mac_work);
+ cancel_delayed_work(&mphy->mac_work);
return;
}
out:
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index acf627aed609..d2a8b2b0df32 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -25,6 +25,8 @@
#define MT_PLE_AC_QEMPTY(_n) MT_PLE(0x500 + 0x40 * (_n))
#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2))
+#define MT_PSE_BASE 0x820c8000
+
/* TMAC: band 0(0x21000), band 1(0xa1000) */
#define MT_WF_TMAC_BASE(_band) ((_band) ? 0x820f4000 : 0x820e4000)
#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs))
@@ -390,6 +392,10 @@
#define MT_CBTOP_RGU_WF_SUBSYS_RST MT_CBTOP_RGU(0x600)
#define MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH BIT(0)
+#define MT7925_CBTOP_RGU_WF_SUBSYS_RST 0x70028600
+#define MT7925_WFSYS_INIT_DONE_ADDR 0x184c1604
+#define MT7925_WFSYS_INIT_DONE 0x00001d1e
+
#define MT_HW_BOUND 0x70010020
#define MT_HW_CHIPID 0x70010200
#define MT_HW_REV 0x70010204
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
index 552808458138..47827d1c5ccb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
@@ -206,6 +206,33 @@ static void mt792xu_epctl_rst_opt(struct mt792x_dev *dev, bool reset)
mt792xu_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val);
}
+struct mt792xu_wfsys_desc {
+ u32 rst_reg;
+ u32 done_reg;
+ u32 done_mask;
+ u32 done_val;
+ u32 delay_ms;
+ bool need_status_sel;
+};
+
+static const struct mt792xu_wfsys_desc mt7921_wfsys_desc = {
+ .rst_reg = MT_CBTOP_RGU_WF_SUBSYS_RST,
+ .done_reg = MT_UDMA_CONN_INFRA_STATUS,
+ .done_mask = MT_UDMA_CONN_WFSYS_INIT_DONE,
+ .done_val = MT_UDMA_CONN_WFSYS_INIT_DONE,
+ .delay_ms = 0,
+ .need_status_sel = true,
+};
+
+static const struct mt792xu_wfsys_desc mt7925_wfsys_desc = {
+ .rst_reg = MT7925_CBTOP_RGU_WF_SUBSYS_RST,
+ .done_reg = MT7925_WFSYS_INIT_DONE_ADDR,
+ .done_mask = U32_MAX,
+ .done_val = MT7925_WFSYS_INIT_DONE,
+ .delay_ms = 20,
+ .need_status_sel = false,
+};
+
int mt792xu_dma_init(struct mt792x_dev *dev, bool resume)
{
int err;
@@ -236,25 +263,33 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init);
int mt792xu_wfsys_reset(struct mt792x_dev *dev)
{
+ const struct mt792xu_wfsys_desc *desc = is_mt7925(&dev->mt76) ?
+ &mt7925_wfsys_desc :
+ &mt7921_wfsys_desc;
u32 val;
int i;
mt792xu_epctl_rst_opt(dev, false);
- val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST);
+ val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg);
val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH;
- mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val);
+ mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val);
- usleep_range(10, 20);
+ if (desc->delay_ms)
+ msleep(desc->delay_ms);
+ else
+ usleep_range(10, 20);
- val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST);
+ val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg);
val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH;
- mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val);
+ mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val);
+
+ if (desc->need_status_sel)
+ mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0);
- mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0);
for (i = 0; i < MT792x_WFSYS_INIT_RETRY_COUNT; i++) {
- val = mt792xu_uhw_rr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS);
- if (val & MT_UDMA_CONN_WFSYS_INIT_DONE)
+ val = mt792xu_uhw_rr(&dev->mt76, desc->done_reg);
+ if ((val & desc->done_mask) == desc->done_val)
break;
msleep(100);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index 76d623b2cafb..34af800964d1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -226,14 +226,23 @@ mt7996_radar_trigger(void *data, u64 val)
#define RADAR_BACKGROUND 2
struct mt7996_dev *dev = data;
struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ);
- int rdd_idx;
+ struct cfg80211_chan_def *chandef;
+ int rdd_idx, ret;
if (!phy || !val || val > RADAR_BACKGROUND)
return -EINVAL;
- if (val == RADAR_BACKGROUND && !dev->rdd2_phy) {
- dev_err(dev->mt76.dev, "Background radar is not enabled\n");
- return -EINVAL;
+ if (test_bit(MT76_SCANNING, &phy->mt76->state))
+ return -EBUSY;
+
+ if (val == RADAR_BACKGROUND) {
+ if (!dev->rdd2_phy || !cfg80211_chandef_valid(&dev->rdd2_chandef)) {
+ dev_err(dev->mt76.dev, "Background radar is not enabled\n");
+ return -EINVAL;
+ }
+ chandef = &dev->rdd2_chandef;
+ } else {
+ chandef = &phy->mt76->chandef;
}
rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND);
@@ -242,6 +251,11 @@ mt7996_radar_trigger(void *data, u64 val)
return -EINVAL;
}
+ ret = cfg80211_chandef_dfs_required(dev->mt76.hw->wiphy, chandef,
+ NL80211_IFTYPE_AP);
+ if (ret <= 0)
+ return ret;
+
return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0);
}
@@ -626,13 +640,18 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
{
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_vif *mvif = msta->vif;
- struct mt7996_dev *dev = mvif->deflink.phy->dev;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink);
struct ieee80211_link_sta *link_sta;
struct seq_file *s = data;
struct ieee80211_vif *vif;
+ struct mt7996_dev *dev;
unsigned int link_id;
+ if (!phy)
+ return;
+
vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv);
+ dev = phy->dev;
rcu_read_lock();
@@ -979,13 +998,17 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
#define LONG_PREAMBLE 1
struct ieee80211_link_sta *link_sta = file->private_data;
struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
- struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct mt7996_phy *link_phy = mt7996_vif_link_phy(&msta->vif->deflink);
struct mt7996_sta_link *msta_link;
struct ra_rate phy = {};
+ struct mt7996_dev *dev;
char buf[100];
int ret;
u16 gi, ltf;
+ if (!link_phy)
+ return -EINVAL;
+
if (count >= sizeof(buf))
return -EINVAL;
@@ -1008,6 +1031,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
* spe - off: 0, on: 1
* ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2
*/
+ dev = link_phy->dev;
if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu",
&phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi,
&phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, &ltf) != 10) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
index 274b273df1ee..8f5d297dafce 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
@@ -128,15 +128,27 @@ static void mt7996_dma_config(struct mt7996_dev *dev)
/* data tx queue */
if (is_mt7996(&dev->mt76)) {
- TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
if (dev->hif2) {
- /* default bn1:ring19 bn2:ring21 */
- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1,
- MT7996_TXQ_BAND1);
- TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2,
- MT7996_TXQ_BAND2);
+ if (mt76_npu_device_active(&dev->mt76)) {
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND2,
+ MT7996_TXQ_BAND2);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND0,
+ MT7996_TXQ_BAND0);
+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1,
+ MT7996_TXQ_BAND1);
+ } else {
+ /* default bn1:ring19 bn2:ring21 */
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0,
+ MT7996_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1,
+ MT7996_TXQ_BAND1);
+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2,
+ MT7996_TXQ_BAND2);
+ }
} else {
/* single pcie bn0/1:ring18 bn2:ring19 */
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0,
+ MT7996_TXQ_BAND0);
TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1,
MT7996_TXQ_BAND1);
}
@@ -350,6 +362,9 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
if (!mt7996_has_wa(dev) || mt76_npu_device_active(&dev->mt76))
irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) |
MT_INT_RX(MT_RXQ_BAND1_WA));
+ if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76))
+ irq_mask &= ~(MT_INT_RX(MT_RXQ_TXFREE_BAND0) |
+ MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2));
irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
mt7996_irq_enable(dev, irq_mask);
@@ -430,39 +445,48 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 |
MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
- if (is_mt7996(&dev->mt76))
- mt76_set(dev, MT_WFDMA_HOST_CONFIG,
- MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
- else
+ if (is_mt7996(&dev->mt76)) {
+ if (mt76_npu_device_active(&dev->mt76))
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+ MT_WFDMA_HOST_CONFIG_BAND0_PCIE1);
+ else
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+ MT_WFDMA_HOST_CONFIG_BAND2_PCIE1);
+ } else {
mt76_set(dev, MT_WFDMA_HOST_CONFIG,
MT_WFDMA_HOST_CONFIG_BAND1_PCIE1);
+ }
/* AXI read outstanding number */
mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL,
MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14);
- if (dev->hif2->speed < PCIE_SPEED_5_0GT ||
- (dev->hif2->speed == PCIE_SPEED_5_0GT &&
- dev->hif2->width < PCIE_LNK_X2)) {
- mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
- WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
- FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
- 0x1));
- mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
- MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
- FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
- 0x1));
- } else if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
- (dev->hif2->speed == PCIE_SPEED_8_0GT &&
- dev->hif2->width < PCIE_LNK_X2)) {
- mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
- WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
- FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
- 0x2));
- mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
- MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
- FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
- 0x2));
+ if (!is_mt7996(&dev->mt76) ||
+ !mt76_npu_device_active(&dev->mt76)) {
+ if (dev->hif2->speed < PCIE_SPEED_5_0GT ||
+ (dev->hif2->speed == PCIE_SPEED_5_0GT &&
+ dev->hif2->width < PCIE_LNK_X2)) {
+ mt76_rmw(dev,
+ WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ 0x1));
+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
+ MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
+ FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
+ 0x1));
+ } else if (dev->hif2->speed < PCIE_SPEED_8_0GT ||
+ (dev->hif2->speed == PCIE_SPEED_8_0GT &&
+ dev->hif2->width < PCIE_LNK_X2)) {
+ mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK,
+ 0x2));
+ mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2,
+ MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
+ FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK,
+ 0x2));
+ }
}
/* WFDMA rx threshold */
@@ -497,7 +521,7 @@ static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset)
int mt7996_dma_rro_init(struct mt7996_dev *dev)
{
struct mt76_dev *mdev = &dev->mt76;
- u32 irq_mask;
+ u32 size;
int ret;
if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) {
@@ -524,7 +548,8 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
mt76_queue_reset(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C],
true);
}
- goto start_hw_rro;
+
+ return 0;
}
/* ind cmd */
@@ -545,10 +570,12 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
if (mtk_wed_device_active(&mdev->mmio.wed) &&
mtk_wed_get_rx_capa(&mdev->mmio.wed))
mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed;
+
+ size = is_mt7996(mdev) && mt76_npu_device_active(mdev)
+ ? MT7996_NPU_RX_RING_SIZE / 4 : MT7996_RX_RING_SIZE;
ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0],
MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0),
- MT7996_RX_RING_SIZE,
- MT7996_RX_MSDU_PAGE_SIZE,
+ size, MT7996_RX_MSDU_PAGE_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0));
if (ret)
return ret;
@@ -560,10 +587,12 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
if (mtk_wed_device_active(&mdev->mmio.wed) &&
mtk_wed_get_rx_capa(&mdev->mmio.wed))
mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed;
+
+ size = is_mt7996(mdev) && mt76_npu_device_active(mdev)
+ ? MT7996_NPU_RX_RING_SIZE / 2 : MT7996_RX_RING_SIZE;
ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1],
MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1),
- MT7996_RX_RING_SIZE,
- MT7996_RX_MSDU_PAGE_SIZE,
+ size, MT7996_RX_MSDU_PAGE_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1));
if (ret)
return ret;
@@ -576,52 +605,60 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
if (mtk_wed_device_active(&mdev->mmio.wed) &&
mtk_wed_get_rx_capa(&mdev->mmio.wed))
mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed;
+
+ size = is_mt7996(mdev) && mt76_npu_device_active(mdev)
+ ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE;
ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2],
MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2),
- MT7996_RX_RING_SIZE,
- MT7996_RX_MSDU_PAGE_SIZE,
+ size, MT7996_RX_MSDU_PAGE_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2));
if (ret)
return ret;
}
-start_hw_rro:
- if (mtk_wed_device_active(&mdev->mmio.wed)) {
- irq_mask = mdev->mmio.irqmask |
+ return 0;
+}
+
+void mt7996_dma_rro_start(struct mt7996_dev *dev)
+{
+ u32 irq_mask;
+
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ irq_mask = dev->mt76.mmio.irqmask |
MT_INT_TX_DONE_BAND2;
mt76_wr(dev, MT_INT_MASK_CSR, irq_mask);
- mtk_wed_device_start_hw_rro(&mdev->mmio.wed, irq_mask, false);
+ mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, irq_mask,
+ false);
mt7996_irq_enable(dev, irq_mask);
- } else {
- if (is_mt7996(&dev->mt76)) {
- mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1,
- mt76_dma_rx_poll);
- mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2,
- mt76_dma_rx_poll);
- mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2,
- mt76_dma_rx_poll);
- } else {
- mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1,
- mt76_dma_rx_poll);
- }
+ return;
+ }
- mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll);
- if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) {
- mt76_queue_rx_init(dev, MT_RXQ_RRO_RXDMAD_C,
- mt76_dma_rx_poll);
- } else {
- mt76_queue_rx_init(dev, MT_RXQ_RRO_IND,
- mt76_dma_rx_poll);
- mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0,
- mt76_dma_rx_poll);
- }
+ if (is_mt7996(&dev->mt76)) {
+ mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1,
+ mt76_dma_rx_poll);
+ mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2,
+ mt76_dma_rx_poll);
+ mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2,
+ mt76_dma_rx_poll);
+ } else {
+ mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1,
+ mt76_dma_rx_poll);
+ }
- if (!mt76_npu_device_active(&dev->mt76))
- mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
+ mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll);
+ if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) {
+ mt76_queue_rx_init(dev, MT_RXQ_RRO_RXDMAD_C,
+ mt76_dma_rx_poll);
+ } else {
+ mt76_queue_rx_init(dev, MT_RXQ_RRO_IND,
+ mt76_dma_rx_poll);
+ mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0,
+ mt76_dma_rx_poll);
}
- return 0;
+ if (!mt76_npu_device_active(&dev->mt76))
+ mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
}
int mt7996_dma_init(struct mt7996_dev *dev)
@@ -642,11 +679,16 @@ int mt7996_dma_init(struct mt7996_dev *dev)
mt7996_dma_disable(dev, true);
/* init tx queue */
- ret = mt7996_init_tx_queues(&dev->phy,
- MT_TXQ_ID(dev->mphy.band_idx),
- MT7996_TX_RING_SIZE,
- MT_TXQ_RING_BASE(0),
- wed);
+ if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76))
+ ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0),
+ MT7996_NPU_TX_RING_SIZE,
+ MT_TXQ_RING_BASE(0) + hif1_ofs,
+ NULL);
+ else
+ ret = mt7996_init_tx_queues(&dev->phy,
+ MT_TXQ_ID(dev->mphy.band_idx),
+ MT7996_TX_RING_SIZE,
+ MT_TXQ_RING_BASE(0), wed);
if (ret)
return ret;
@@ -714,6 +756,9 @@ int mt7996_dma_init(struct mt7996_dev *dev)
(is_mt7992(&dev->mt76)))) {
dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed;
+ } else if (is_mt7992(&dev->mt76) &&
+ mt76_npu_device_active(&dev->mt76)) {
+ dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_NPU_Q_TXFREE(0);
}
if (mt7996_has_wa(dev)) {
@@ -846,6 +891,8 @@ int mt7996_dma_init(struct mt7996_dev *dev)
/* tx free notify event from WA for band0 */
dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE;
dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed;
+ } else if (mt76_npu_device_active(&dev->mt76)) {
+ dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_NPU_Q_TXFREE(0);
}
ret = mt76_queue_alloc(dev,
@@ -859,16 +906,21 @@ int mt7996_dma_init(struct mt7996_dev *dev)
}
if (mt7996_band_valid(dev, MT_BAND2)) {
+ u32 size;
+
/* rx rro data queue for band2 */
dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags =
MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN;
if (mtk_wed_device_active(wed) &&
mtk_wed_get_rx_capa(wed))
dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed;
+
+ size = is_mt7996(&dev->mt76) &&
+ mt76_npu_device_active(&dev->mt76)
+ ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE;
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2],
MT_RXQ_ID(MT_RXQ_RRO_BAND2),
- MT7996_RX_RING_SIZE,
- MT7996_RX_BUF_SIZE,
+ size, MT7996_RX_BUF_SIZE,
MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + hif1_ofs);
if (ret)
return ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index 8f60772913b4..ac05f7d75d63 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -33,6 +33,8 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev)
if (dev->var.fem == MT7996_FEM_INT)
return MT7992_EEPROM_DEFAULT_23_INT;
return MT7992_EEPROM_DEFAULT_23;
+ case MT7992_VAR_TYPE_24:
+ return MT7992_EEPROM_DEFAULT_24;
case MT7992_VAR_TYPE_44:
default:
if (dev->var.fem == MT7996_FEM_INT)
@@ -153,7 +155,7 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default)
dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
- dev->flash_mode = true;
+ dev->eeprom_mode = EEPROM_MODE_DEFAULT_BIN;
out:
release_firmware(fw);
@@ -163,26 +165,31 @@ out:
static int mt7996_eeprom_load(struct mt7996_dev *dev)
{
+ u32 eeprom_blk_size, block_num;
bool use_default = false;
- int ret;
+ int ret, i;
ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
if (ret < 0)
return ret;
if (ret && !mt7996_check_eeprom(dev)) {
- dev->flash_mode = true;
+ dev->eeprom_mode = EEPROM_MODE_FLASH;
goto out;
}
- if (!dev->flash_mode) {
- u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
- u32 block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE);
+ if (mt7996_has_ext_eeprom(dev)) {
+ /* external eeprom mode */
+ dev->eeprom_mode = EEPROM_MODE_EXT;
+ eeprom_blk_size = MT7996_EXT_EEPROM_BLOCK_SIZE;
+ } else {
u8 free_block_num;
- int i;
- memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE);
- ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
+ /* efuse mode */
+ dev->eeprom_mode = EEPROM_MODE_EFUSE;
+ eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
+ ret = mt7996_mcu_get_efuse_free_block(dev, &free_block_num);
if (ret < 0)
return ret;
@@ -191,27 +198,29 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
use_default = true;
goto out;
}
+ }
+
+ /* check if eeprom data from fw is valid */
+ if (mt7996_mcu_get_eeprom(dev, 0, NULL, eeprom_blk_size,
+ dev->eeprom_mode) ||
+ mt7996_check_eeprom(dev)) {
+ use_default = true;
+ goto out;
+ }
+
+ /* read eeprom data from fw */
+ block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
+ for (i = 1; i < block_num; i++) {
+ u32 len = eeprom_blk_size;
- /* check if eeprom data from fw is valid */
- if (mt7996_mcu_get_eeprom(dev, 0, NULL, 0) ||
- mt7996_check_eeprom(dev)) {
+ if (i == block_num - 1)
+ len = MT7996_EEPROM_SIZE % eeprom_blk_size;
+ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size,
+ NULL, len, dev->eeprom_mode);
+ if (ret && ret != -EINVAL) {
use_default = true;
goto out;
}
-
- /* read eeprom data from fw */
- for (i = 1; i < block_num; i++) {
- u32 len = eeprom_blk_size;
-
- if (i == block_num - 1)
- len = MT7996_EEPROM_SIZE % eeprom_blk_size;
- ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size,
- NULL, len);
- if (ret && ret != -EINVAL) {
- use_default = true;
- goto out;
- }
- }
}
out:
@@ -385,7 +394,8 @@ bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev)
return false;
break;
case MT7992_DEVICE_ID:
- if (dev->var.type == MT7992_VAR_TYPE_23)
+ if (dev->var.type == MT7992_VAR_TYPE_23 ||
+ dev->var.type == MT7992_VAR_TYPE_24)
return false;
break;
case MT7990_DEVICE_ID: {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
index 00a8286bd136..d6f9aa1ab52d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -34,6 +34,20 @@ static const struct ieee80211_iface_combination if_comb_global = {
BIT(NL80211_CHAN_WIDTH_40) |
BIT(NL80211_CHAN_WIDTH_80) |
BIT(NL80211_CHAN_WIDTH_160),
+ .beacon_int_min_gcd = 100,
+};
+
+static const struct ieee80211_iface_combination if_comb_global_7992 = {
+ .limits = &if_limits_global,
+ .n_limits = 1,
+ .max_interfaces = 32,
+ .num_different_channels = MT7996_MAX_RADIOS - 1,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
+ .beacon_int_min_gcd = 100,
};
static const struct ieee80211_iface_limit if_limits[] = {
@@ -85,6 +99,7 @@ static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = {
.extended_capabilities_mask = if_types_ext_capa_ap,
.extended_capabilities_len = sizeof(if_types_ext_capa_ap),
.mld_capa_and_ops =
+ FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_FREQ_SEP_TYPE_IND, 1) |
FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS,
MT7996_MAX_RADIOS - 1),
},
@@ -485,7 +500,8 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
hw->vif_data_size = sizeof(struct mt7996_vif);
hw->chanctx_data_size = sizeof(struct mt76_chanctx);
- wiphy->iface_combinations = &if_comb_global;
+ wiphy->iface_combinations = is_mt7996(&dev->mt76) ? &if_comb_global :
+ &if_comb_global_7992;
wiphy->n_iface_combinations = 1;
wiphy->radio = dev->radios;
@@ -521,8 +537,10 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
hw->max_tx_fragments = 4;
+ wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */
/* init led callbacks */
if (IS_ENABLED(CONFIG_MT76_LEDS)) {
@@ -592,7 +610,7 @@ static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev)
void mt7996_mac_init(struct mt7996_dev *dev)
{
#define HIF_TXD_V2_1 0x21
- int i;
+ int i, rx_path_type;
mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
@@ -606,11 +624,16 @@ void mt7996_mac_init(struct mt7996_dev *dev)
}
/* rro module init */
- if (dev->hif2)
+ if (dev->hif2) {
+ if (mt76_npu_device_active(&dev->mt76))
+ rx_path_type = is_mt7996(&dev->mt76) ? 6 : 8;
+ else
+ rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7;
mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE,
- is_mt7996(&dev->mt76) ? 2 : 7);
- else
+ rx_path_type);
+ } else {
mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 0);
+ }
if (mt7996_has_hwrro(dev)) {
u16 timeout;
@@ -668,8 +691,9 @@ static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band)
return 0;
if (dev->hif2 &&
- ((is_mt7996(&dev->mt76) && band == MT_BAND2) ||
- (is_mt7992(&dev->mt76) && band == MT_BAND1))) {
+ ((is_mt7992(&dev->mt76) && band == MT_BAND1) ||
+ (is_mt7996(&dev->mt76) && band == MT_BAND2 &&
+ !mt76_npu_device_active(&dev->mt76)))) {
hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
wed = &dev->mt76.mmio.wed_hif2;
}
@@ -709,14 +733,19 @@ static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band)
/* init wiphy according to mphy and phy */
mt7996_init_wiphy_band(mphy->hw, phy);
- if (is_mt7996(&dev->mt76) && !dev->hif2 && band == MT_BAND1) {
+ if (is_mt7996(&dev->mt76) &&
+ ((band == MT_BAND1 && !dev->hif2) ||
+ (band == MT_BAND2 && mt76_npu_device_active(&dev->mt76)))) {
int i;
for (i = 0; i <= MT_TXQ_PSD; i++)
- mphy->q_tx[i] = dev->mt76.phys[MT_BAND0]->q_tx[0];
+ mphy->q_tx[i] = dev->mt76.phys[band - 1]->q_tx[0];
} else {
- ret = mt7996_init_tx_queues(mphy->priv, MT_TXQ_ID(band),
- MT7996_TX_RING_SIZE,
+ int size = is_mt7996(&dev->mt76) &&
+ mt76_npu_device_active(&dev->mt76)
+ ? MT7996_NPU_TX_RING_SIZE / 2 : MT7996_TX_RING_SIZE;
+
+ ret = mt7996_init_tx_queues(mphy->priv, MT_TXQ_ID(band), size,
MT_TXQ_RING_BASE(band) + hif1_ofs,
wed);
if (ret)
@@ -756,15 +785,41 @@ static void mt7996_init_work(struct work_struct *work)
mt7996_mcu_set_eeprom(dev);
mt7996_mac_init(dev);
mt7996_txbf_init(dev);
+
+ if (!is_mt7990(&dev->mt76))
+ mt7996_mcu_set_dup_wtbl(dev);
}
void mt7996_wfsys_reset(struct mt7996_dev *dev)
{
- mt76_set(dev, MT_WF_SUBSYS_RST, 0x1);
- msleep(20);
+ if (!is_mt7990(&dev->mt76)) {
+ mt76_set(dev, MT_WF_SUBSYS_RST, 0x1);
+ msleep(20);
+
+ mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1);
+ msleep(20);
+
+ return;
+ }
+
+ if (!dev->recovery.hw_full_reset)
+ return;
- mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1);
+ mt76_set(dev, MT_WF_SUBSYS_RST,
+ MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT |
+ MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT |
+ MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT);
+ mt76_rmw(dev, MT_WF_SUBSYS_RST,
+ MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE,
+ u32_encode_bits(0x20, MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE));
+ mt76_clear(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK);
+ mt76_set(dev, MT_WF_SUBSYS_RST, MT_WF_SUBSYS_RST_WHOLE_PATH_RST);
msleep(20);
+
+ if (mt76_poll(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK, 0x1a, 1000))
+ return;
+
+ dev_err(dev->mt76.dev, "wfsys reset fail\n");
}
static void mt7996_rro_hw_init_v3(struct mt7996_dev *dev)
@@ -842,8 +897,7 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
}
} else {
/* set emul 3.0 function */
- mt76_wr(dev, MT_RRO_3_0_EMU_CONF,
- MT_RRO_3_0_EMU_CONF_EN_MASK);
+ mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK);
mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0,
dev->wed_rro.addr_elem[0].phy_addr);
@@ -935,6 +989,12 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
addr++;
}
+ if (is_mt7996(&dev->mt76) &&
+ mt76_npu_device_active(&dev->mt76))
+ mt76_npu_send_txrx_addr(&dev->mt76, 0, i,
+ dev->wed_rro.addr_elem[i].phy_addr,
+ 0, 0);
+
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
if (mtk_wed_device_active(&dev->mt76.mmio.wed) &&
mtk_wed_get_rx_capa(&dev->mt76.mmio.wed)) {
@@ -995,6 +1055,10 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
addr++;
}
+ if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76))
+ mt76_npu_send_txrx_addr(&dev->mt76, 1, 0,
+ dev->wed_rro.session.phy_addr, 0, 0);
+
mt7996_rro_hw_init(dev);
return mt7996_dma_rro_init(dev);
@@ -1081,8 +1145,12 @@ static void mt7996_wed_rro_work(struct work_struct *work)
list);
list_del_init(&e->list);
- if (mt76_npu_device_active(&dev->mt76))
+ if (mt76_npu_device_active(&dev->mt76)) {
+ if (is_mt7996(&dev->mt76))
+ mt76_npu_send_txrx_addr(&dev->mt76, 3, e->id,
+ 0, 0, 0);
goto reset_session;
+ }
for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) {
void *ptr = dev->wed_rro.session.ptr;
@@ -1129,7 +1197,7 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev)
else if (u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992))
var_type = MT7992_VAR_TYPE_44;
else
- return -EINVAL;
+ var_type = MT7992_VAR_TYPE_24;
break;
case MT7990_DEVICE_ID:
var_type = MT7990_VAR_TYPE_23;
@@ -1163,7 +1231,8 @@ static int mt7996_variant_fem_init(struct mt7996_dev *dev)
if (ret)
return ret;
- ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf));
+ ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf),
+ EEPROM_MODE_EFUSE);
if (ret && ret != -EINVAL)
return ret;
@@ -1696,6 +1765,8 @@ int mt7996_register_device(struct mt7996_dev *dev)
if (ret)
return ret;
+ mt7996_dma_rro_start(dev);
+
ret = mt76_register_device(&dev->mt76, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
@@ -1726,6 +1797,7 @@ error:
void mt7996_unregister_device(struct mt7996_dev *dev)
{
+ cancel_work_sync(&dev->dump_work);
cancel_work_sync(&dev->wed_rro.work);
mt7996_unregister_phy(mt7996_phy3(dev));
mt7996_unregister_phy(mt7996_phy2(dev));
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 84cbf36b493c..e2a83da3a09c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -13,45 +13,6 @@
#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
-static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- .radar_pattern = {
- [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 },
- [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 },
- [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 },
- [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 },
- [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 },
- [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 },
- [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 },
- [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 },
- },
-};
-
-static const struct mt7996_dfs_radar_spec fcc_radar_specs = {
- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- .radar_pattern = {
- [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 },
- [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 },
- [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 },
- [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 },
- [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 },
- },
-};
-
-static const struct mt7996_dfs_radar_spec jp_radar_specs = {
- .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
- .radar_pattern = {
- [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 },
- [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 },
- [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 },
- [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 },
- [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 },
- [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 },
- [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 },
- [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 },
- },
-};
-
static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
u16 idx, u8 band_idx)
{
@@ -527,7 +488,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
!(csum_status & (BIT(0) | BIT(2) | BIT(3))))
skb->ip_summed = CHECKSUM_UNNECESSARY;
- if (rxd1 & MT_RXD3_NORMAL_FCS_ERR)
+ if (rxd3 & MT_RXD3_NORMAL_FCS_ERR)
status->flag |= RX_FLAG_FAILED_FCS_CRC;
if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
@@ -700,6 +661,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
hdr = mt76_skb_get_hdr(skb);
fc = hdr->frame_control;
+ if (ieee80211_is_beacon(fc))
+ mt76_rx_beacon(mphy, skb);
if (ieee80211_is_data_qos(fc)) {
u8 *qos = ieee80211_get_qos_ctl(hdr);
@@ -1139,10 +1102,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
* req
*/
if (le32_to_cpu(ptr[7]) & MT_TXD7_MAC_TXD) {
- u32 val;
+ u32 val, mac_txp_size = sizeof(struct mt76_connac_hw_txp);
ptr = (__le32 *)(txwi + MT_TXD_SIZE);
- memset((void *)ptr, 0, sizeof(struct mt76_connac_fw_txp));
+ memset((void *)ptr, 0, mac_txp_size);
val = FIELD_PREP(MT_TXP0_TOKEN_ID0, id) |
MT_TXP0_TOKEN_ID0_VALID_MASK;
@@ -1161,6 +1124,8 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
tx_info->buf[1].addr >> 32);
#endif
ptr[3] = cpu_to_le32(val);
+
+ tx_info->buf[0].len = MT_TXD_SIZE + mac_txp_size;
} else {
struct mt76_connac_txp_common *txp;
@@ -1270,8 +1235,9 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta,
if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA)))
return;
- if (!test_and_set_bit(tid, &wcid->ampdu_state))
- ieee80211_start_tx_ba_session(link_sta->sta, tid, 0);
+ if (!test_and_set_bit(tid, &wcid->ampdu_state) &&
+ ieee80211_start_tx_ba_session(link_sta->sta, tid, 0))
+ clear_bit(tid, &wcid->ampdu_state);
}
static void
@@ -2203,9 +2169,14 @@ mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
for_each_vif_active_link(vif, link_conf, link_id) {
struct mt7996_vif_link *link;
+ struct mt7996_phy *link_phy;
link = mt7996_vif_link(dev, vif, link_id);
- if (!link || link->phy != phy)
+ if (!link)
+ continue;
+
+ link_phy = mt7996_vif_link_phy(link);
+ if (link_phy != phy)
continue;
mt7996_mcu_add_beacon(dev->mt76.hw, vif, link_conf,
@@ -2248,6 +2219,12 @@ void mt7996_tx_token_put(struct mt7996_dev *dev)
}
spin_unlock_bh(&dev->mt76.token_lock);
idr_destroy(&dev->mt76.token);
+
+ for (id = 0; id < __MT_MAX_BAND; id++) {
+ struct mt76_phy *phy = dev->mt76.phys[id];
+ if (phy)
+ atomic_set(&phy->mgmt_tx_pending, 0);
+ }
}
static int
@@ -2395,23 +2372,8 @@ mt7996_mac_reset_sta_iter(void *data, struct ieee80211_sta *sta)
struct mt7996_dev *dev = data;
int i;
- for (i = 0; i < ARRAY_SIZE(msta->link); i++) {
- struct mt7996_sta_link *msta_link = NULL;
-
- msta_link = rcu_replace_pointer(msta->link[i], msta_link,
- lockdep_is_held(&dev->mt76.mutex));
- if (!msta_link)
- continue;
-
- mt7996_mac_sta_deinit_link(dev, msta_link);
-
- if (msta->deflink_id == i) {
- msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
- continue;
- }
-
- kfree_rcu(msta_link, rcu_head);
- }
+ for (i = 0; i < ARRAY_SIZE(msta->link); i++)
+ mt7996_mac_sta_remove_link(dev, sta, i, true);
}
static void
@@ -2544,6 +2506,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
if (mtk_wed_device_active(&dev->mt76.mmio.wed))
mtk_wed_device_stop(&dev->mt76.mmio.wed);
+ mt7996_npu_hw_stop(dev);
ieee80211_stop_queues(mt76_hw(dev));
set_bit(MT76_RESET, &dev->mphy.state);
@@ -2564,14 +2527,19 @@ void mt7996_mac_reset_work(struct work_struct *work)
mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
continue;
+ if (mt76_npu_device_active(&dev->mt76) &&
+ mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
+ continue;
+
+ if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i]))
+ continue;
+
napi_disable(&dev->mt76.napi[i]);
}
napi_disable(&dev->mt76.tx_napi);
mutex_lock(&dev->mt76.mutex);
- mt7996_npu_hw_stop(dev);
-
mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
@@ -2591,7 +2559,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
mt7996_dma_start(dev, false, false);
if (!is_mt7996(&dev->mt76) && dev->mt76.hwrro_mode == MT76_HWRRO_V3)
- mt76_wr(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK);
+ mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK);
if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
u32 wed_irq_mask = MT_INT_TX_DONE_BAND2 |
@@ -2610,6 +2578,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
MT_INT_TX_RX_DONE_EXT);
}
+ __mt7996_npu_hw_init(dev);
+
clear_bit(MT76_MCU_RESET, &dev->mphy.state);
mt7996_for_each_phy(dev, phy)
clear_bit(MT76_RESET, &phy->mt76->state);
@@ -2619,6 +2589,13 @@ void mt7996_mac_reset_work(struct work_struct *work)
mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
continue;
+ if (mt76_npu_device_active(&dev->mt76) &&
+ mt76_queue_is_wed_rro(&dev->mt76.q_rx[i]))
+ continue;
+
+ if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i]))
+ continue;
+
napi_enable(&dev->mt76.napi[i]);
local_bh_disable();
napi_schedule(&dev->mt76.napi[i]);
@@ -2639,8 +2616,6 @@ void mt7996_mac_reset_work(struct work_struct *work)
mutex_unlock(&dev->mt76.mutex);
- mt7996_npu_hw_init(dev);
-
mt7996_for_each_phy(dev, phy)
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
MT7996_WATCHDOG_TIME);
@@ -2737,6 +2712,11 @@ void mt7996_reset(struct mt7996_dev *dev)
return;
}
+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA) {
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ }
+
queue_work(dev->mt76.wq, &dev->reset_work);
wake_up(&dev->reset_wait);
}
@@ -2955,6 +2935,7 @@ void mt7996_mac_work(struct work_struct *work)
mutex_unlock(&mphy->dev->mutex);
+ mt76_beacon_mon_check(mphy);
mt76_tx_status_check(mphy->dev, false);
ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
@@ -2974,7 +2955,7 @@ static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy)
static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx)
{
- int err, region;
+ int region;
switch (dev->mt76.region) {
case NL80211_DFS_ETSI:
@@ -2989,11 +2970,7 @@ static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx)
break;
}
- err = mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region);
- if (err < 0)
- return err;
-
- return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, rdd_idx, 1);
+ return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region);
}
static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy)
@@ -3015,40 +2992,6 @@ static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy)
return err;
}
-static int
-mt7996_dfs_init_radar_specs(struct mt7996_phy *phy)
-{
- const struct mt7996_dfs_radar_spec *radar_specs;
- struct mt7996_dev *dev = phy->dev;
- int err, i;
-
- switch (dev->mt76.region) {
- case NL80211_DFS_FCC:
- radar_specs = &fcc_radar_specs;
- err = mt7996_mcu_set_fcc5_lpn(dev, 8);
- if (err < 0)
- return err;
- break;
- case NL80211_DFS_ETSI:
- radar_specs = &etsi_radar_specs;
- break;
- case NL80211_DFS_JP:
- radar_specs = &jp_radar_specs;
- break;
- default:
- return -EINVAL;
- }
-
- for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) {
- err = mt7996_mcu_set_radar_th(dev, i,
- &radar_specs->radar_pattern[i]);
- if (err < 0)
- return err;
- }
-
- return mt7996_mcu_set_pulse_th(dev, &radar_specs->pulse_th);
-}
-
int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy)
{
struct mt7996_dev *dev = phy->dev;
@@ -3068,10 +3011,6 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy)
goto stop;
if (prev_state <= MT_DFS_STATE_DISABLED) {
- err = mt7996_dfs_init_radar_specs(phy);
- if (err < 0)
- return err;
-
err = mt7996_dfs_start_radar_detector(phy);
if (err < 0)
return err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
index 4eca37b013fc..70ee30f32f88 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
@@ -37,9 +37,4 @@ struct mt7996_dfs_pattern {
u32 min_stgpr_diff;
} __packed;
-struct mt7996_dfs_radar_spec {
- struct mt7996_dfs_pulse pulse_th;
- struct mt7996_dfs_pattern radar_pattern[16];
-};
-
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index f16135f0b7f9..a8a6552d49f6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -56,7 +56,7 @@ static int mt7996_start(struct ieee80211_hw *hw)
mutex_lock(&dev->mt76.mutex);
ret = mt7996_mcu_set_hdr_trans(dev, true);
- if (!ret && is_mt7992(&dev->mt76)) {
+ if (!ret && !is_mt7996(&dev->mt76)) {
u8 queue = mt76_connac_lmac_mapping(IEEE80211_AC_VI);
ret = mt7996_mcu_cp_support(dev, queue);
@@ -79,6 +79,7 @@ static void mt7996_stop_phy(struct mt7996_phy *phy)
mutex_lock(&dev->mt76.mutex);
+ mt7996_mcu_rdd_resume_tx(phy);
mt7996_mcu_set_radio_en(phy, false);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
@@ -238,10 +239,13 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
link_conf = &vif->bss_conf;
if (cmd == SET_KEY && !sta && !link->mt76.cipher) {
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
+
link->mt76.cipher =
mt76_connac_mcu_get_cipher(key->cipher);
- mt7996_mcu_add_bss_info(link->phy, vif, link_conf,
- &link->mt76, msta_link, true);
+ if (phy)
+ mt7996_mcu_add_bss_info(phy, vif, link_conf,
+ &link->mt76, msta_link, true);
}
if (cmd == SET_KEY)
@@ -300,9 +304,16 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
.cmd = SET_KEY,
.link_id = link_conf->link_id,
};
- struct mt76_txq *mtxq;
int mld_idx, idx, ret;
+ if ((mvif->mt76.valid_links & BIT(link_conf->link_id)) &&
+ !mlink->offchannel) {
+ if (vif->type == NL80211_IFTYPE_AP)
+ return mt7996_mcu_mld_link_oper(dev, link_conf, link,
+ true);
+ return 0;
+ }
+
mlink->idx = __ffs64(~dev->mt76.vif_mask);
if (mlink->idx >= mt7996_max_interface_num(dev))
return -ENOSPC;
@@ -316,7 +327,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
return -ENOSPC;
link->mld_idx = mld_idx;
- link->phy = phy;
mlink->omac_idx = idx;
mlink->band_idx = band_idx;
mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3;
@@ -343,11 +353,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
mt7996_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- if (vif->txq) {
- mtxq = (struct mt76_txq *)vif->txq->drv_priv;
- mtxq->wcid = idx;
- }
-
if (vif->type != NL80211_IFTYPE_AP &&
(!mlink->omac_idx || mlink->omac_idx > 3))
vif->offload_flags = 0;
@@ -370,56 +375,66 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it);
- if (!mlink->wcid->offchannel &&
- mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED)
- mvif->mt76.deflink_id = link_conf->link_id;
+ if (!mlink->wcid->offchannel) {
+ if (vif->txq &&
+ mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) {
+ struct mt76_txq *mtxq;
+
+ mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+ mvif->mt76.deflink_id = link_conf->link_id;
+ mtxq->wcid = idx;
+ }
+ mvif->mt76.valid_links |= BIT(link_conf->link_id);
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+
+ if (vif->cfg.assoc && link_conf->beacon_int) {
+ mlink->beacon_mon_interval =
+ msecs_to_jiffies(ieee80211_tu_to_usec(
+ link_conf->beacon_int) / 1000);
+ WRITE_ONCE(mlink->beacon_mon_last, jiffies);
+ }
+ }
return 0;
}
-void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *link_conf,
- struct mt76_vif_link *mlink)
+static void mt7996_vif_link_destroy(struct mt7996_phy *phy,
+ struct mt7996_vif_link *link,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
{
- struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76);
struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_sta_link *msta_link = &link->msta_link;
- struct mt7996_phy *phy = mphy->priv;
- struct mt7996_dev *dev = phy->dev;
+ unsigned int link_id = msta_link->wcid.link_id;
+ struct mt76_vif_link *mlink = &link->mt76;
struct mt7996_key_iter_data it = {
.cmd = SET_KEY,
- .link_id = link_conf->link_id,
+ .link_id = link_id,
};
+ struct mt7996_dev *dev = phy->dev;
int idx = msta_link->wcid.idx;
+ if (!link_conf)
+ link_conf = &vif->bss_conf;
+
if (!mlink->wcid->offchannel)
- ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it);
+ ieee80211_iter_keys(phy->mt76->hw, vif, mt7996_key_iter, &it);
mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
CONN_STATE_DISCONNECT, false);
mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false);
-
mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false);
rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
- if (!mlink->wcid->offchannel &&
- mvif->mt76.deflink_id == link_conf->link_id) {
- struct ieee80211_bss_conf *iter;
- unsigned int link_id;
-
- mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED;
- for_each_vif_active_link(vif, iter, link_id) {
- if (link_id != IEEE80211_LINK_UNSPECIFIED) {
- mvif->mt76.deflink_id = link_id;
- break;
- }
- }
- }
-
dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx);
dev->mld_idx_mask &= ~BIT_ULL(link->mld_idx);
phy->omac_mask &= ~BIT_ULL(mlink->omac_idx);
+ if (!mlink->wcid->offchannel)
+ mvif->mt76.valid_links &= ~BIT(link_id);
spin_lock_bh(&dev->mt76.sta_poll_lock);
if (!list_empty(&msta_link->wcid.poll_list))
@@ -427,6 +442,58 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
spin_unlock_bh(&dev->mt76.sta_poll_lock);
mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid);
+
+ if (mlink != (struct mt76_vif_link *)vif->drv_priv &&
+ !mlink->wcid->offchannel) {
+ rcu_assign_pointer(mlink->mvif->link[link_id], NULL);
+ kfree_rcu(mlink, rcu_head);
+ }
+}
+
+void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct mt76_vif_link *mlink)
+{
+ struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta_link *msta_link = &link->msta_link;
+ unsigned int link_id = msta_link->wcid.link_id;
+ struct mt7996_phy *phy = mphy->priv;
+
+ /* Hw requires to destroy active links tearing down the interface, so
+ * postpone it removing the interface.
+ */
+ if (mlink->wcid->offchannel) {
+ mt7996_vif_link_destroy(phy, link, vif, link_conf);
+ } else {
+ if (vif->type == NL80211_IFTYPE_AP) {
+ mt7996_mcu_mld_reconf_stop_link(phy->dev, vif,
+ BIT(link_id));
+ mt7996_mcu_mld_link_oper(phy->dev, link_conf, link,
+ false);
+ }
+
+ if (vif->txq && mvif->mt76.deflink_id == link_id) {
+ struct ieee80211_bss_conf *iter;
+ struct mt76_txq *mtxq;
+
+ mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+ /* Primary link will be removed, look for a new one */
+ for_each_vif_active_link(vif, iter, link_id) {
+ if (link_id == msta_link->wcid.link_id)
+ continue;
+
+ link = mt7996_vif_link(phy->dev, vif, link_id);
+ if (!link)
+ continue;
+
+ mtxq->wcid = link->msta_link.wcid.idx;
+ mvif->mt76.deflink_id = link_id;
+ break;
+ }
+ }
+ }
}
static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy)
@@ -472,6 +539,8 @@ static void mt7996_set_monitor(struct mt7996_phy *phy, bool enabled)
mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx),
MT_DMA_DCR0_RXD_G5_EN, enabled);
+ mt76_rmw_field(dev, MT_MDP_DCR0,
+ MT_MDP_DCR0_RX_HDR_TRANS_EN, !enabled);
mt7996_phy_set_rxfilter(phy);
mt7996_mcu_set_sniffer_mode(phy, enabled);
}
@@ -530,10 +599,29 @@ static void mt7996_remove_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
static void mt7996_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ unsigned long rem_links = mvif->mt76.valid_links;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_radio_data rdata = {};
+ unsigned int link_id;
int i;
+ /* Remove all active links */
+ for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_vif_link *link;
+ struct mt7996_phy *phy;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ phy = __mt7996_phy(dev, link->msta_link.wcid.phy_idx);
+ if (!phy)
+ continue;
+
+ mt7996_vif_link_destroy(phy, link, vif, NULL);
+ }
+
ieee80211_iterate_active_interfaces_mtx(hw, 0, mt7996_remove_iter,
&rdata);
mt76_vif_cleanup(&dev->mt76, vif);
@@ -811,15 +899,24 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
for_each_vif_active_link(vif, link_conf, link_id) {
struct mt7996_vif_link *link;
+ struct mt7996_phy *phy;
link = mt7996_vif_link(dev, vif, link_id);
if (!link)
continue;
- if (!link->phy)
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ link->mt76.beacon_mon_interval =
+ msecs_to_jiffies(ieee80211_tu_to_usec(
+ link_conf->beacon_int) / 1000);
+ WRITE_ONCE(link->mt76.beacon_mon_last, jiffies);
+ }
+
+ phy = mt7996_vif_link_phy(link);
+ if (!phy)
continue;
- mt7996_mcu_add_bss_info(link->phy, vif, link_conf,
+ mt7996_mcu_add_bss_info(phy, vif, link_conf,
&link->mt76, &link->msta_link,
true);
mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
@@ -828,6 +925,20 @@ mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
}
+ if ((changed & BSS_CHANGED_ASSOC) && !vif->cfg.assoc &&
+ vif->type == NL80211_IFTYPE_STATION) {
+ struct ieee80211_bss_conf *link_conf;
+ unsigned long link_id;
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ struct mt7996_vif_link *link;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (link)
+ link->mt76.beacon_mon_interval = 0;
+ }
+ }
+
mutex_unlock(&dev->mt76.mutex);
}
@@ -864,6 +975,10 @@ mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
!!(changed & BSS_CHANGED_BSSID));
}
+ if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7996_mcu_set_protection(phy, link, info->ht_operation_mode,
+ info->use_cts_prot);
+
if (changed & BSS_CHANGED_ERP_SLOT) {
int slottime = info->use_short_slot ? 9 : 20;
@@ -927,10 +1042,82 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef)
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band);
+ struct ieee80211_bss_conf *link_conf;
+ unsigned int link_id;
mutex_lock(&dev->mt76.mutex);
- mt7996_mcu_add_beacon(hw, vif, &vif->bss_conf, vif->bss_conf.enable_beacon);
+
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ struct mt7996_vif_link *link;
+ struct mt7996_phy *link_phy;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ link_phy = mt7996_vif_link_phy(link);
+ if (link_phy != phy)
+ continue;
+
+ /* Reset beacon when channel switch triggered during CAC to let
+ * FW correctly perform CSA countdown
+ */
+ if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->mt76->chandef,
+ vif->type))
+ mt7996_mcu_add_beacon(hw, vif, link_conf, false);
+
+ mt7996_mcu_add_beacon(hw, vif, link_conf, true);
+ break;
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static int
+mt7996_post_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf)
+{
+ struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band);
+ struct mt7996_vif_link *link;
+ int ret = -EINVAL;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ link = mt7996_vif_conf_link(dev, vif, link_conf);
+ if (!link)
+ goto out;
+
+ ret = mt7996_mcu_update_bss_rfch(phy, link);
+ if (ret)
+ goto out;
+
+ ieee80211_iterate_stations_mtx(hw, mt7996_mcu_update_sta_rec_bw, link);
+
+ ret = mt7996_mcu_rdd_resume_tx(phy);
+
+out:
mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static void
+mt7996_sta_init_txq_wcid(struct ieee80211_sta *sta, int idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ struct mt76_txq *mtxq;
+
+ if (!sta->txq[i])
+ continue;
+
+ mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
+ mtxq->wcid = idx;
+ }
}
static int
@@ -941,30 +1128,22 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
{
struct ieee80211_sta *sta = link_sta->sta;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt7996_phy *phy = link->phy;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
struct mt7996_sta_link *msta_link;
int idx;
+ if (!phy)
+ return -EINVAL;
+
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
if (idx < 0)
return -ENOSPC;
if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) {
- int i;
-
msta_link = &msta->deflink;
msta->deflink_id = link_id;
msta->seclink_id = msta->deflink_id;
-
- for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
- struct mt76_txq *mtxq;
-
- if (!sta->txq[i])
- continue;
-
- mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
- mtxq->wcid = idx;
- }
+ mt7996_sta_init_txq_wcid(sta, idx);
} else {
msta_link = kzalloc_obj(*msta_link);
if (!msta_link)
@@ -1000,9 +1179,17 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
return 0;
}
-void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
- struct mt7996_sta_link *msta_link)
+void mt7996_mac_sta_remove_link(struct mt7996_dev *dev,
+ struct ieee80211_sta *sta,
+ unsigned int link_id, bool flush)
{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta_link *msta_link;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ return;
+
spin_lock_bh(&dev->mt76.sta_poll_lock);
if (!list_empty(&msta_link->wcid.poll_list))
list_del_init(&msta_link->wcid.poll_list);
@@ -1011,51 +1198,61 @@ void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
spin_unlock_bh(&dev->mt76.sta_poll_lock);
mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid);
- mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx);
-}
-
-static void
-mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, unsigned long links)
-{
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
- struct mt76_dev *mdev = &dev->mt76;
- unsigned int link_id;
- for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
- struct mt7996_sta_link *msta_link = NULL;
- struct mt7996_vif_link *link;
- struct mt76_phy *mphy;
-
- msta_link = rcu_replace_pointer(msta->link[link_id], msta_link,
- lockdep_is_held(&mdev->mutex));
- if (!msta_link)
- continue;
+ if (msta_link->wcid.link_valid) {
+ struct mt7996_phy *phy;
mt7996_mac_wtbl_update(dev, msta_link->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
- mt7996_mac_sta_deinit_link(dev, msta_link);
- link = mt7996_vif_link(dev, vif, link_id);
- if (!link)
- continue;
+ phy = __mt7996_phy(dev, msta_link->wcid.phy_idx);
+ if (phy)
+ phy->mt76->num_sta--;
- mphy = mt76_vif_link_phy(&link->mt76);
- if (!mphy)
- continue;
-
- mphy->num_sta--;
if (msta->deflink_id == link_id) {
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
- continue;
+ if (msta->seclink_id == link_id) {
+ /* no secondary link available */
+ msta->seclink_id = msta->deflink_id;
+ } else {
+ struct mt7996_sta_link *msta_seclink;
+
+ /* switch to the secondary link */
+ msta_seclink = mt76_dereference(
+ msta->link[msta->seclink_id],
+ &dev->mt76);
+ if (msta_seclink) {
+ msta->deflink_id = msta->seclink_id;
+ mt7996_sta_init_txq_wcid(sta,
+ msta_seclink->wcid.idx);
+ }
+ }
} else if (msta->seclink_id == link_id) {
- msta->seclink_id = IEEE80211_LINK_UNSPECIFIED;
+ msta->seclink_id = msta->deflink_id;
}
+ msta_link->wcid.link_valid = false;
+ }
- kfree_rcu(msta_link, rcu_head);
+ if (flush) {
+ rcu_assign_pointer(msta->link[link_id], NULL);
+ rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], NULL);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx);
+ if (msta_link != &msta->deflink)
+ kfree_rcu(msta_link, rcu_head);
}
}
+static void
+mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, unsigned long links,
+ bool flush)
+{
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS)
+ mt7996_mac_sta_remove_link(dev, sta, link_id, flush);
+}
+
static int
mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, unsigned long new_links)
@@ -1067,11 +1264,15 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) {
struct ieee80211_bss_conf *link_conf;
struct ieee80211_link_sta *link_sta;
+ struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
struct mt76_phy *mphy;
- if (rcu_access_pointer(msta->link[link_id]))
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (msta_link) {
+ msta_link->wcid.link_valid = true;
continue;
+ }
link_conf = link_conf_dereference_protected(vif, link_id);
if (!link_conf) {
@@ -1108,7 +1309,7 @@ mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
return 0;
error_unlink:
- mt7996_mac_sta_remove_links(dev, vif, sta, new_links);
+ mt7996_mac_sta_remove_links(dev, vif, sta, new_links, true);
return err;
}
@@ -1125,7 +1326,7 @@ mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mutex_lock(&dev->mt76.mutex);
- mt7996_mac_sta_remove_links(dev, vif, sta, rem);
+ mt7996_mac_sta_remove_links(dev, vif, sta, rem, false);
ret = mt7996_mac_sta_add_links(dev, vif, sta, add);
mutex_unlock(&dev->mt76.mutex);
@@ -1234,10 +1435,12 @@ static void
mt7996_mac_sta_remove(struct mt7996_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
- unsigned long links = sta->valid_links ? sta->valid_links : BIT(0);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ int i;
mutex_lock(&dev->mt76.mutex);
- mt7996_mac_sta_remove_links(dev, vif, sta, links);
+ for (i = 0; i < ARRAY_SIZE(msta->link); i++)
+ mt7996_mac_sta_remove_link(dev, sta, i, true);
mutex_unlock(&dev->mt76.mutex);
}
@@ -1489,8 +1692,8 @@ mt7996_get_stats(struct ieee80211_hw *hw,
u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link)
{
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_phy *phy = link->phy;
union {
u64 t64;
u32 t32[2];
@@ -1549,7 +1752,7 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
: link->mt76.omac_idx;
- phy = link->phy;
+ phy = mt7996_vif_link_phy(link);
if (!phy)
goto unlock;
@@ -1583,7 +1786,7 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (!link)
goto unlock;
- phy = link->phy;
+ phy = mt7996_vif_link_phy(link);
if (!phy)
goto unlock;
@@ -1713,9 +1916,14 @@ static void mt7996_link_rate_ctrl_update(void *data,
struct mt7996_sta_link *msta_link)
{
struct mt7996_sta *msta = msta_link->sta;
- struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(&msta->vif->deflink);
+ struct mt7996_dev *dev;
u32 *changed = data;
+ if (!phy)
+ return;
+
+ dev = phy->dev;
spin_lock_bh(&dev->mt76.sta_poll_lock);
msta_link->changed |= *changed;
@@ -2204,6 +2412,10 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
path->mtk_wdma.wdma_idx = wed->wdma_idx;
else
#endif
+ if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76) &&
+ msta_link->wcid.phy_idx == MT_BAND2)
+ path->mtk_wdma.wdma_idx = 1;
+ else
path->mtk_wdma.wdma_idx = link->mt76.band_idx;
path->mtk_wdma.bss = link->mt76.idx;
path->mtk_wdma.queue = 0;
@@ -2275,6 +2487,21 @@ mt7996_reconfig_complete(struct ieee80211_hw *hw,
MT7996_WATCHDOG_TIME);
}
+static int
+mt7996_set_eml_op_mode(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_eml_params *eml_params)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ int ret;
+
+ mutex_lock(&dev->mt76.mutex);
+ ret = mt7996_mcu_set_emlsr_mode(dev, vif, sta, eml_params);
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
const struct ieee80211_ops mt7996_ops = {
.add_chanctx = mt76_add_chanctx,
.remove_chanctx = mt76_remove_chanctx,
@@ -2306,6 +2533,7 @@ const struct ieee80211_ops mt7996_ops = {
.release_buffered_frames = mt76_release_buffered_frames,
.get_txpower = mt7996_get_txpower,
.channel_switch_beacon = mt7996_channel_switch_beacon,
+ .post_channel_switch = mt7996_post_channel_switch,
.get_stats = mt7996_get_stats,
.get_et_sset_count = mt7996_get_et_sset_count,
.get_et_stats = mt7996_get_et_stats,
@@ -2337,4 +2565,5 @@ const struct ieee80211_ops mt7996_ops = {
.change_vif_links = mt7996_change_vif_links,
.change_sta_links = mt7996_mac_sta_change_links,
.reconfig_complete = mt7996_reconfig_complete,
+ .set_eml_op_mode = mt7996_set_eml_op_mode,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index b4422a4754cd..16420375112d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -18,6 +18,9 @@
case MT7992_VAR_TYPE_23: \
_fw = MT7992_##name##_23; \
break; \
+ case MT7992_VAR_TYPE_24: \
+ _fw = MT7992_##name##_24; \
+ break; \
default: \
_fw = MT7992_##name; \
} \
@@ -125,9 +128,16 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link,
__le16 *he_mcs, u16 mcs_map)
{
+ struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76);
int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss;
- enum nl80211_band band = link->phy->mt76->chandef.chan->band;
- const u16 *mask = link->bitrate_mask.control[band].he_mcs;
+ enum nl80211_band band;
+ const u16 *mask;
+
+ if (!mphy)
+ return;
+
+ band = mphy->chandef.chan->band;
+ mask = link->bitrate_mask.control[band].he_mcs;
for (nss = 0; nss < max_nss; nss++) {
int mcs;
@@ -209,6 +219,7 @@ static int
mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd,
struct sk_buff *skb, int seq)
{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
struct mt7996_mcu_rxd *rxd;
struct mt7996_mcu_uni_event *event;
int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
@@ -217,6 +228,14 @@ mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd,
if (!skb) {
dev_err(mdev->dev, "Message %08x (seq %d) timeout\n",
cmd, seq);
+
+ if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) {
+ dev->recovery.restart = true;
+ wake_up(&dev->mt76.mcu.wait);
+ queue_work(dev->mt76.wq, &dev->reset_work);
+ wake_up(&dev->reset_wait);
+ }
+
return -ETIMEDOUT;
}
@@ -233,7 +252,7 @@ mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd,
event = (struct mt7996_mcu_uni_event *)skb->data;
ret = le32_to_cpu(event->status);
/* skip invalid event */
- if (mcu_cmd != event->cid)
+ if (mcu_cmd != le16_to_cpu(event->cid))
ret = -EAGAIN;
} else {
skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
@@ -259,7 +278,8 @@ mt7996_mcu_set_timeout(struct mt76_dev *mdev, int cmd)
mdev->mcu.timeout = 2 * HZ;
return;
case MCU_UNI_CMD_EFUSE_CTRL:
- mdev->mcu.timeout = 20 * HZ;
+ case MCU_UNI_CMD_EXT_EEPROM_CTRL:
+ mdev->mcu.timeout = 30 * HZ;
return;
default:
break;
@@ -313,13 +333,12 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
uni_txd->pkt_type = MCU_PKT_ID;
uni_txd->seq = seq;
- if (cmd & __MCU_CMD_FIELD_QUERY)
- uni_txd->option = MCU_CMD_UNI_QUERY_ACK;
- else
- uni_txd->option = MCU_CMD_UNI_EXT_ACK;
+ uni_txd->option = MCU_CMD_UNI;
+ if (!(cmd & __MCU_CMD_FIELD_QUERY))
+ uni_txd->option |= MCU_CMD_SET;
- if (mcu_cmd == MCU_UNI_CMD_SDO)
- uni_txd->option &= ~MCU_CMD_ACK;
+ if (wait_seq)
+ uni_txd->option |= MCU_CMD_ACK;
if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM))
uni_txd->s2d_index = MCU_S2D_H2CN;
@@ -390,13 +409,117 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
sizeof(req), false);
}
+struct mt7996_mcu_countdown_data {
+ struct mt76_phy *mphy;
+ u8 omac_idx;
+};
+
static void
mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
- if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
+ struct mt7996_mcu_countdown_data *cdata = (void *)priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct ieee80211_bss_conf *link_conf = NULL;
+ unsigned long valid_links = vif->valid_links ?: BIT(0);
+ unsigned int link_id;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ return;
+
+ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt76_vif_link *mlink =
+ rcu_dereference(mvif->mt76.link[link_id]);
+
+ if (mlink && mlink->band_idx == cdata->mphy->band_idx &&
+ mlink->omac_idx == cdata->omac_idx) {
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ break;
+ }
+ }
+
+ if (!link_conf || !link_conf->csa_active)
+ return;
+
+ ieee80211_csa_finish(vif, link_conf->link_id);
+}
+
+static void
+mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct mt7996_mcu_countdown_data *cdata = (void *)priv;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct ieee80211_bss_conf *link_conf = NULL;
+ unsigned long valid_links = vif->valid_links ?: BIT(0);
+ unsigned int link_id;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ return;
+
+ for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt76_vif_link *mlink =
+ rcu_dereference(mvif->mt76.link[link_id]);
+
+ if (mlink && mlink->band_idx == cdata->mphy->band_idx &&
+ mlink->omac_idx == cdata->omac_idx) {
+ link_conf = rcu_dereference(vif->link_conf[link_id]);
+ break;
+ }
+ }
+
+ if (!link_conf || !link_conf->color_change_active)
+ return;
+
+ ieee80211_color_change_finish(vif, link_conf->link_id);
+}
+
+static void
+mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+#define UNI_EVENT_IE_COUNTDOWN_CSA 0
+#define UNI_EVENT_IE_COUNTDOWN_BCC 1
+ struct header {
+ u8 band;
+ u8 rsv[3];
+ };
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ const char *data = (char *)&rxd[1], *tail;
+ struct header *hdr = (struct header *)data;
+ struct tlv *tlv = (struct tlv *)(data + 4);
+ struct mt7996_mcu_countdown_notify *event;
+ struct mt7996_mcu_countdown_data cdata;
+
+ if (hdr->band >= ARRAY_SIZE(dev->mt76.phys))
return;
- ieee80211_csa_finish(vif, 0);
+ cdata.mphy = dev->mt76.phys[hdr->band];
+ if (!cdata.mphy)
+ return;
+
+ tail = skb->data + skb->len;
+ data += sizeof(*hdr);
+ while (data + sizeof(*tlv) < tail && le16_to_cpu(tlv->len)) {
+ event = (struct mt7996_mcu_countdown_notify *)tlv->data;
+
+ cdata.omac_idx = event->omac_idx;
+
+ switch (le16_to_cpu(tlv->tag)) {
+ case UNI_EVENT_IE_COUNTDOWN_CSA:
+ ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_csa_finish, &cdata);
+ break;
+ case UNI_EVENT_IE_COUNTDOWN_BCC:
+ ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev),
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_cca_finish, &cdata);
+ break;
+ default:
+ break;
+ }
+
+ data += le16_to_cpu(tlv->len);
+ tlv = (struct tlv *)data;
+ }
}
static void
@@ -416,24 +539,32 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
break;
case MT_RDD_IDX_BACKGROUND:
if (!dev->rdd2_phy)
- return;
+ goto err;
mphy = dev->rdd2_phy->mt76;
break;
default:
- dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx);
- return;
+ goto err;
}
if (!mphy)
- return;
+ goto err;
- if (r->rdd_idx == MT_RDD_IDX_BACKGROUND)
+ if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) {
cfg80211_background_radar_event(mphy->hw->wiphy,
&dev->rdd2_chandef,
GFP_ATOMIC);
- else
+ } else {
+ struct mt7996_phy *phy = mphy->priv;
+
+ phy->rdd_tx_paused = true;
ieee80211_radar_detected(mphy->hw, NULL);
+ }
dev->hw_pattern++;
+
+ return;
+
+err:
+ dev_err(dev->mt76.dev, "Invalid RDD idx %d\n", r->rdd_idx);
}
static void
@@ -476,57 +607,6 @@ out:
wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data);
}
-static void
-mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
-{
- if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
- return;
-
- ieee80211_color_change_finish(vif, 0);
-}
-
-static void
-mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
-{
-#define UNI_EVENT_IE_COUNTDOWN_CSA 0
-#define UNI_EVENT_IE_COUNTDOWN_BCC 1
- struct header {
- u8 band;
- u8 rsv[3];
- };
- struct mt76_phy *mphy = &dev->mt76.phy;
- struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
- const char *data = (char *)&rxd[1], *tail;
- struct header *hdr = (struct header *)data;
- struct tlv *tlv = (struct tlv *)(data + 4);
-
- if (hdr->band >= ARRAY_SIZE(dev->mt76.phys))
- return;
-
- if (hdr->band && dev->mt76.phys[hdr->band])
- mphy = dev->mt76.phys[hdr->band];
-
- tail = skb->data + skb->len;
- data += sizeof(struct header);
- while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) {
- switch (le16_to_cpu(tlv->tag)) {
- case UNI_EVENT_IE_COUNTDOWN_CSA:
- ieee80211_iterate_active_interfaces_atomic(mphy->hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7996_mcu_csa_finish, mphy->hw);
- break;
- case UNI_EVENT_IE_COUNTDOWN_BCC:
- ieee80211_iterate_active_interfaces_atomic(mphy->hw,
- IEEE80211_IFACE_ITER_RESUME_ALL,
- mt7996_mcu_cca_finish, mphy->hw);
- break;
- }
-
- data += le16_to_cpu(tlv->len);
- tlv = (struct tlv *)data;
- }
-}
-
static int
mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate)
{
@@ -1170,6 +1250,123 @@ out:
MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
}
+int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, struct mt7996_vif_link *link)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct sk_buff *skb;
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ mt7996_mcu_bss_rfch_tlv(skb, phy);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link,
+ u8 ht_mode, bool use_cts_prot)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct bss_prot_tlv *prot;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ enum {
+ PROT_NONMEMBER = BIT(1),
+ PROT_20MHZ = BIT(2),
+ PROT_NONHT_MIXED = BIT(3),
+ PROT_LEGACY_ERP = BIT(5),
+ PROT_NONGF_STA = BIT(7),
+ };
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PROTECT_INFO,
+ sizeof(*prot));
+ prot = (struct bss_prot_tlv *)tlv;
+
+ switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) {
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER:
+ prot->prot_mode = cpu_to_le32(PROT_NONMEMBER);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+ prot->prot_mode = cpu_to_le32(PROT_20MHZ);
+ break;
+ case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+ prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED);
+ break;
+ }
+
+ if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA);
+
+ if (use_cts_prot)
+ prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_eml_params *eml_params)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_sta_link *msta_link;
+ struct sta_rec_eml_op *eml_op;
+ struct mt7996_vif_link *link;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+ msta_link = mt76_dereference(msta->link[eml_params->link_id],
+ &dev->mt76);
+ if (!msta_link)
+ return -EINVAL;
+
+ link = mt7996_vif_link(dev, vif, eml_params->link_id);
+ if (!link)
+ return -EINVAL;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EML_OP, sizeof(*eml_op));
+ eml_op = (struct sta_rec_eml_op *)tlv;
+ eml_op->link_bitmap = 0;
+
+ if (eml_params->control & IEEE80211_EML_CTRL_EMLSR_MODE) {
+ unsigned long link_bitmap = eml_params->link_bitmap;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &link_bitmap,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt76_phy *mphy;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ mphy = mt76_vif_link_phy(&link->mt76);
+ if (!mphy)
+ continue;
+
+ eml_op->link_bitmap |= BIT(mphy->band_idx);
+ }
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf)
{
@@ -1779,9 +1976,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
#define EBF_MODE BIT(0)
#define IBF_MODE BIT(1)
#define BF_MAT_ORDER 4
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
struct ieee80211_vif *vif = link_conf->vif;
- struct mt7996_phy *phy = link->phy;
- int tx_ant = hweight16(phy->mt76->chainmask) - 1;
struct sta_rec_bf *bf;
struct tlv *tlv;
static const u8 matrix[BF_MAT_ORDER][BF_MAT_ORDER] = {
@@ -1790,8 +1986,12 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
{2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */
{3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */
};
+ int tx_ant;
bool ebf;
+ if (!phy)
+ return;
+
if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he))
return;
@@ -1807,17 +2007,18 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
* ht: iBF only, since mac80211 lacks of eBF support
*/
if (link_sta->eht_cap.has_eht)
- mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf);
+ mt7996_mcu_sta_bfer_eht(link_sta, vif, phy, bf, ebf);
else if (link_sta->he_cap.has_he)
- mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf);
+ mt7996_mcu_sta_bfer_he(link_sta, vif, phy, bf, ebf);
else if (link_sta->vht_cap.vht_supported)
- mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf);
+ mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf);
else if (link_sta->ht_cap.ht_supported)
- mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf);
+ mt7996_mcu_sta_bfer_ht(link_sta, phy, bf, ebf);
else
return;
bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0);
+ tx_ant = hweight16(phy->mt76->chainmask) - 1;
if (is_mt7992(&dev->mt76) && tx_ant == 4)
bf->bf_cap |= IBF_MODE;
@@ -1849,11 +2050,14 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
struct ieee80211_link_sta *link_sta,
struct mt7996_vif_link *link)
{
- struct mt7996_phy *phy = link->phy;
- int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
struct sta_rec_bfee *bfee;
struct tlv *tlv;
u8 nrow = 0;
+ int tx_ant;
+
+ if (!phy)
+ return;
if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he))
return;
@@ -1877,6 +2081,7 @@ mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
}
/* reply with identity matrix to avoid 2x2 BF negative gain */
+ tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2);
}
@@ -2060,6 +2265,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta,
struct ieee80211_sta *sta;
int ret, nrates = 0, idx;
enum nl80211_band band;
+ struct mt76_phy *mphy;
bool has_he;
#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \
@@ -2093,7 +2299,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta,
if (!link_sta)
goto error_unlock;
- band = link->phy->mt76->chandef.chan->band;
+ mphy = mt76_vif_link_phy(&link->mt76);
+ if (!mphy)
+ goto error_unlock;
+
+ band = mphy->chandef.chan->band;
has_he = link_sta->he_cap.has_he;
mask = link->bitrate_mask;
idx = msta_link->wcid.idx;
@@ -2173,18 +2383,25 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev,
struct mt7996_vif_link *link)
{
#define INIT_RCPI 180
- struct mt76_phy *mphy = link->phy->mt76;
- struct cfg80211_chan_def *chandef = &mphy->chandef;
+ struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76);
struct cfg80211_bitrate_mask *mask = &link->bitrate_mask;
u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0;
- enum nl80211_band band = chandef->chan->band;
+ struct cfg80211_chan_def *chandef;
struct sta_rec_ra_uni *ra;
+ enum nl80211_band band;
struct tlv *tlv;
- u32 supp_rate = link_sta->supp_rates[band];
+ u32 supp_rate;
+
+ if (!mphy)
+ return;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
ra = (struct sta_rec_ra_uni *)tlv;
+ chandef = &mphy->chandef;
+ band = chandef->chan->band;
+ supp_rate = link_sta->supp_rates[band];
+
ra->valid = true;
ra->auto_rate = true;
ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta);
@@ -2366,6 +2583,72 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct mt7996_vif_link *link,
sizeof(req), true);
}
+int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ u16 removed_links)
+{
+ unsigned long rem_links = removed_links;
+ struct mld_reconf_stop_link *sl;
+ struct mld_req_hdr hdr = {};
+ unsigned int link_id;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(hdr) + sizeof(*sl));
+ if (!skb)
+ return -ENOMEM;
+
+ memcpy(hdr.mld_addr, vif->addr, ETH_ALEN);
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_STOP_LINK,
+ sizeof(*sl));
+ sl = (struct mld_reconf_stop_link *)tlv;
+ sl->link_bitmap = cpu_to_le16(removed_links);
+
+ for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt7996_vif_link *link;
+
+ link = mt7996_vif_link(dev, vif, link_id);
+ if (!link)
+ continue;
+
+ sl->bss_idx[link_id] = link->mt76.idx;
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD),
+ true);
+}
+
+int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link, bool add)
+{
+ struct ieee80211_vif *vif = link_conf->vif;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_mld_link_op_tlv *mld_op;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD_LINK_OP,
+ sizeof(*mld_op));
+ mld_op = (struct bss_mld_link_op_tlv *)tlv;
+ mld_op->link_operation = add;
+ mld_op->own_mld_id = link->mld_idx;
+ mld_op->link_id = link_conf->link_id;
+ mld_op->group_mld_id = add ? mvif->mld_group_idx : 0xff;
+ mld_op->remap_idx = add ? mvif->mld_remap_idx : 0xff;
+ memcpy(mld_op->mac_addr, vif->addr, ETH_ALEN);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
static void
mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif,
@@ -2529,6 +2812,53 @@ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
}
+void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta)
+{
+ struct mt7996_vif_link *link = (struct mt7996_vif_link *)data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_phy *phy = mt7996_vif_link_phy(link);
+ struct mt7996_sta_link *msta_link;
+ struct mt7996_dev *dev;
+ struct ieee80211_bss_conf *link_conf;
+ struct ieee80211_link_sta *link_sta;
+ struct ieee80211_vif *vif;
+ struct sk_buff *skb;
+ int link_id;
+
+ if (!phy)
+ return;
+
+ if (link->mt76.mvif != &msta->vif->mt76)
+ return;
+
+ dev = phy->dev;
+ link_id = link->msta_link.wcid.link_id;
+ link_sta = link_sta_dereference_protected(sta, link_id);
+ if (!link_sta)
+ return;
+
+ msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ if (!msta_link)
+ return;
+
+ vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+ link_conf = link_conf_dereference_protected(vif, link_id);
+ if (!link_conf)
+ return;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76,
+ &msta_link->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return;
+
+ mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, link);
+ mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link);
+
+ mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
static int
mt7996_mcu_sta_key_tlv(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct sk_buff *skb,
@@ -2676,6 +3006,16 @@ mt7996_mcu_beacon_cntdwn(struct sk_buff *rskb, struct sk_buff *skb,
info = (struct bss_bcn_cntdwn_tlv *)tlv;
info->cnt = skb->data[offs->cntdwn_counter_offs[0]];
+
+ /* abort the CCA countdown when starting CSA countdown */
+ if (csa) {
+ struct bss_bcn_cntdwn_tlv *cca_info;
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_BCC,
+ sizeof(*cca_info));
+ cca_info = (struct bss_bcn_cntdwn_tlv *)tlv;
+ cca_info->cca.abort = true;
+ }
}
static void
@@ -2768,6 +3108,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
{
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf);
+ struct mt76_phy *mphy = link ? mt76_vif_link_phy(&link->mt76) : NULL;
struct mt76_vif_link *mlink = link ? &link->mt76 : NULL;
struct ieee80211_mutable_offsets offs;
struct ieee80211_tx_info *info;
@@ -2782,7 +3123,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (!mlink)
return -EINVAL;
- if (link->phy && link->phy->mt76->offchannel)
+ if (mphy && mphy->offchannel)
enabled = false;
rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink,
@@ -2833,9 +3174,9 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
{
#define OFFLOAD_TX_MODE_SU BIT(0)
#define OFFLOAD_TX_MODE_MU BIT(1)
+ struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76);
struct ieee80211_vif *vif = link_conf->vif;
struct ieee80211_hw *hw = mt76_hw(dev);
- struct mt7996_phy *phy = link->phy;
struct mt76_wcid *wcid = &dev->mt76.global_wcid;
struct bss_inband_discovery_tlv *discov;
struct ieee80211_tx_info *info;
@@ -2846,10 +3187,10 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
u8 *buf, interval;
int len;
- if (!phy)
+ if (!mphy)
return -EINVAL;
- chandef = &phy->mt76->chandef;
+ chandef = &mphy->chandef;
band = chandef->chan->band;
if (link_conf->nontransmitted)
@@ -2887,7 +3228,7 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
info = IEEE80211_SKB_CB(skb);
info->control.vif = vif;
info->band = band;
- info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+ info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mphy->band_idx);
len = ALIGN(sizeof(*discov) + MT_TXD_SIZE + skb->len, 4);
tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len);
@@ -3742,8 +4083,7 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR)
req.switch_reason = CH_SWITCH_NORMAL;
- else if (phy->mt76->offchannel ||
- phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE)
+ else if (phy->mt76->offchannel || !phy->mt76->chanctx)
req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
NL80211_IFTYPE_AP))
@@ -3764,23 +4104,173 @@ int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
&req, sizeof(req), true);
}
-static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
+static int
+mt7996_mcu_get_cal_free_data(struct mt7996_dev *dev)
+{
+#define MT_EE_7977BN_OFFSET (0x1200 - 0x500)
+ struct cal_free_data {
+ u16 adie_offs;
+ u16 eep_offs;
+ };
+ static const struct cal_free_data cal_7975[] = {
+ { 0x5cd, 0x451 }, { 0x5cf, 0x453 }, { 0x5d1, 0x455 },
+ { 0x5d3, 0x457 }, { 0x6c0, 0x44c }, { 0x6c1, 0x44d },
+ { 0x6c2, 0x44e }, { 0x6c3, 0x44f }, { 0x7a1, 0xba1 },
+ { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 }, { 0x7aa, 0xbaa },
+ };
+ static const struct cal_free_data cal_7976[] = {
+ { 0x4c, 0x44c }, { 0x4d, 0x44d }, { 0x4e, 0x44e },
+ { 0x4f, 0x44f }, { 0x50, 0x450 }, { 0x51, 0x451 },
+ { 0x53, 0x453 }, { 0x55, 0x455 }, { 0x57, 0x457 },
+ { 0x59, 0x459 }, { 0x70, 0x470 }, { 0x71, 0x471 },
+ { 0x790, 0xb90 }, { 0x791, 0xb91 }, { 0x794, 0xb94 },
+ { 0x795, 0xb95 }, { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 },
+ { 0x7aa, 0xbaa },
+ };
+ static const struct cal_free_data cal_7977[] = {
+ { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e },
+ { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 },
+ { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 },
+ { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a },
+ { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c },
+ { 0x7d, 0x127d }, { 0x7e, 0x127e },
+ };
+ static const struct cal_free_data cal_7978[] = {
+ { 0x91, 0xb91 }, { 0x95, 0xb95 }, { 0x100, 0x480 },
+ { 0x102, 0x482 }, { 0x104, 0x484 }, { 0x106, 0x486 },
+ { 0x107, 0x487 }, { 0x108, 0x488 }, { 0x109, 0x489 },
+ { 0x10a, 0x48a }, { 0x10b, 0x48b }, { 0x10c, 0x48c },
+ { 0x10e, 0x48e }, { 0x110, 0x490 },
+ };
+ static const struct cal_free_data cal_7979[] = {
+ { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e },
+ { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 },
+ { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 },
+ { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a },
+ { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c },
+ { 0x7e, 0x127e }, { 0x80, 0x1280 },
+ };
+ const struct cal_free_data *cal_arr[__MT_MAX_BAND];
+ u16 cal_arr_len[__MT_MAX_BAND] = {};
+ u8 *eeprom = (u8 *)dev->mt76.eeprom.data;
+ int band, i, ret;
+
+#define CAL_ARR(_band, _adie) do { \
+ cal_arr[_band] = cal_##_adie; \
+ cal_arr_len[_band] = ARRAY_SIZE(cal_##_adie); \
+ } while (0)
+
+ switch (mt76_chip(&dev->mt76)) {
+ case MT7996_DEVICE_ID:
+ /* adie 0 */
+ if (dev->var.fem == MT7996_FEM_INT &&
+ dev->var.type != MT7996_VAR_TYPE_233)
+ CAL_ARR(0, 7975);
+ else
+ CAL_ARR(0, 7976);
+
+ /* adie 1 */
+ if (dev->var.type == MT7996_VAR_TYPE_444)
+ CAL_ARR(1, 7977);
+
+ /* adie 2 */
+ CAL_ARR(2, 7977);
+ break;
+ case MT7992_DEVICE_ID:
+ /* adie 0 */
+ if (dev->var.type == MT7992_VAR_TYPE_44 &&
+ dev->var.fem != MT7996_FEM_EXT)
+ CAL_ARR(0, 7975);
+ else if (dev->var.type == MT7992_VAR_TYPE_24)
+ CAL_ARR(0, 7978);
+ else
+ CAL_ARR(0, 7976);
+
+ /* adie 1 */
+ if (dev->var.type == MT7992_VAR_TYPE_44 &&
+ dev->var.fem != MT7996_FEM_INT)
+ CAL_ARR(1, 7977);
+ else if (dev->var.type != MT7992_VAR_TYPE_23)
+ CAL_ARR(1, 7979);
+ break;
+ case MT7990_DEVICE_ID:
+ /* adie 0 */
+ CAL_ARR(0, 7976);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (band = 0; band < __MT_MAX_BAND; band++) {
+ u8 buf[MT7996_EEPROM_BLOCK_SIZE];
+ const struct cal_free_data *cal;
+ u16 prev_block_idx = -1;
+ u16 adie_base;
+
+ if (!cal_arr_len[band])
+ continue;
+
+ if (band == MT_BAND0)
+ adie_base = MT7996_EFUSE_BASE_OFFS_ADIE0;
+ else if (band == MT_BAND1 && is_mt7992(&dev->mt76))
+ adie_base = MT7992_EFUSE_BASE_OFFS_ADIE1;
+ else if (band == MT_BAND1)
+ adie_base = MT7996_EFUSE_BASE_OFFS_ADIE1;
+ else
+ adie_base = MT7996_EFUSE_BASE_OFFS_ADIE2;
+
+ cal = cal_arr[band];
+ for (i = 0; i < cal_arr_len[band]; i++) {
+ u16 adie_offset = cal[i].adie_offs + adie_base;
+ u16 eep_offset = cal[i].eep_offs;
+ u16 block_idx = adie_offset / MT7996_EEPROM_BLOCK_SIZE;
+ u16 offset = adie_offset % MT7996_EEPROM_BLOCK_SIZE;
+
+ if (is_mt7996(&dev->mt76) && band == MT_BAND1 &&
+ dev->var.type == MT7996_VAR_TYPE_444)
+ eep_offset -= MT_EE_7977BN_OFFSET;
+
+ if (prev_block_idx != block_idx) {
+ memset(buf, 0, sizeof(buf));
+ ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf,
+ MT7996_EEPROM_BLOCK_SIZE,
+ EEPROM_MODE_EFUSE);
+ if (ret) {
+ if (ret != -EINVAL)
+ return ret;
+ prev_block_idx = -1;
+ continue;
+ }
+ }
+ eeprom[eep_offset] = buf[offset];
+ prev_block_idx = block_idx;
+ }
+ }
+
+ return 0;
+}
+
+int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
{
#define MAX_PAGE_IDX_MASK GENMASK(7, 5)
#define PAGE_IDX_MASK GENMASK(4, 2)
#define PER_PAGE_SIZE 0x400
- struct mt7996_mcu_eeprom req = {
+ struct mt7996_mcu_eeprom_update req = {
.tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
.buffer_mode = EE_MODE_BUFFER
};
u16 eeprom_size = MT7996_EEPROM_SIZE;
u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE);
u8 *eep = (u8 *)dev->mt76.eeprom.data;
- int eep_len, i;
+ int ret, eep_len, i;
+
+ ret = mt7996_mcu_get_cal_free_data(dev);
+ if (ret)
+ return ret;
for (i = 0; i < total; i++, eep += eep_len) {
struct sk_buff *skb;
- int ret, msg_len;
+ int msg_len;
if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE))
eep_len = eeprom_size % PER_PAGE_SIZE;
@@ -3809,59 +4299,66 @@ static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
return 0;
}
-int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
-{
- struct mt7996_mcu_eeprom req = {
- .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
- .len = cpu_to_le16(sizeof(req) - 4),
- .buffer_mode = EE_MODE_EFUSE,
- .format = EE_FORMAT_WHOLE
- };
-
- if (dev->flash_mode)
- return mt7996_mcu_set_eeprom_flash(dev);
-
- return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
- &req, sizeof(req), true);
-}
-
-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len)
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len,
+ enum mt7996_eeprom_mode mode)
{
- struct {
- u8 _rsv[4];
-
- __le16 tag;
- __le16 len;
- __le32 addr;
- __le32 valid;
- u8 data[16];
- } __packed req = {
- .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
- .len = cpu_to_le16(sizeof(req) - 4),
- .addr = cpu_to_le32(round_down(offset,
- MT7996_EEPROM_BLOCK_SIZE)),
+ struct mt7996_mcu_eeprom_access req = {
+ .info.len = cpu_to_le16(sizeof(req) - 4),
};
+ struct mt7996_mcu_eeprom_access_event *event;
struct sk_buff *skb;
- bool valid;
- int ret;
+ int ret, cmd;
+ u32 addr;
+
+ switch (mode) {
+ case EEPROM_MODE_EFUSE:
+ addr = round_down(offset, MT7996_EEPROM_BLOCK_SIZE);
+ cmd = MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL);
+ req.info.tag = cpu_to_le16(UNI_EFUSE_ACCESS);
+ break;
+ case EEPROM_MODE_EXT:
+ addr = round_down(offset, MT7996_EXT_EEPROM_BLOCK_SIZE);
+ cmd = MCU_WM_UNI_CMD_QUERY(EXT_EEPROM_CTRL);
+ req.info.tag = cpu_to_le16(UNI_EXT_EEPROM_ACCESS);
+ req.eeprom.ext_eeprom.data_len = cpu_to_le32(buf_len);
+ break;
+ default:
+ return -EINVAL;
+ }
- ret = mt76_mcu_send_and_get_msg(&dev->mt76,
- MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
- &req, sizeof(req), true, &skb);
+ req.info.addr = cpu_to_le32(addr);
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, cmd, &req, sizeof(req),
+ true, &skb);
if (ret)
return ret;
- valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
- if (valid) {
- u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
+ event = (struct mt7996_mcu_eeprom_access_event *)skb->data;
+ if (event->valid) {
+ u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
+
+ addr = le32_to_cpu(event->addr);
if (!buf)
buf = (u8 *)dev->mt76.eeprom.data + addr;
- if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE)
- buf_len = MT7996_EEPROM_BLOCK_SIZE;
- skb_pull(skb, 48);
- memcpy(buf, skb->data, buf_len);
+ switch (mode) {
+ case EEPROM_MODE_EFUSE:
+ if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE)
+ buf_len = MT7996_EEPROM_BLOCK_SIZE;
+
+ memcpy(buf, event->eeprom.efuse, buf_len);
+ break;
+ case EEPROM_MODE_EXT:
+ if (!buf_len || buf_len > MT7996_EXT_EEPROM_BLOCK_SIZE)
+ buf_len = MT7996_EXT_EEPROM_BLOCK_SIZE;
+
+ memcpy(buf, event->eeprom.ext_eeprom.data,
+ ret_len < buf_len ? ret_len : buf_len);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
} else {
ret = -EINVAL;
}
@@ -3871,7 +4368,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
return ret;
}
-int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num)
{
struct {
u8 _rsv[4];
@@ -3903,7 +4400,6 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
{
-#define NIC_CAP 3
#define UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION 0x21
struct {
u8 _rsv[4];
@@ -3911,7 +4407,7 @@ int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
__le16 tag;
__le16 len;
} __packed req = {
- .tag = cpu_to_le16(NIC_CAP),
+ .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA),
.len = cpu_to_le16(sizeof(req) - 4),
};
struct sk_buff *skb;
@@ -4559,6 +5055,35 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
&req, sizeof(req), true);
}
+int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy)
+{
+ struct {
+ u8 band_idx;
+ u8 _rsv[3];
+
+ __le16 tag;
+ __le16 len;
+ u8 mac_enable;
+ u8 _rsv2[3];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_BAND_CONFIG_MAC_ENABLE_CTRL),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .mac_enable = 2,
+ };
+ int ret;
+
+ if (!phy->rdd_tx_paused)
+ return 0;
+
+ ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ &req, sizeof(req), true);
+ if (!ret)
+ phy->rdd_tx_paused = false;
+
+ return ret;
+}
+
int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val)
{
struct {
@@ -4889,3 +5414,25 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode)
return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT),
&cp_mode, sizeof(cp_mode), true);
}
+
+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev)
+{
+#define DUP_WTBL_NUM 80
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le16 base;
+ __le16 num;
+ u8 _rsv2[4];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_CHIP_CONFIG_DUP_WTBL),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1),
+ .num = cpu_to_le16(DUP_WTBL_NUM),
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req,
+ sizeof(req), true);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index e0b83ac9f5e2..8902e16508b7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -25,8 +25,8 @@ struct mt7996_mcu_rxd {
};
struct mt7996_mcu_uni_event {
- u8 cid;
- u8 __rsv[3];
+ __le16 cid;
+ u8 __rsv[2];
__le32 status; /* 0: success, others: fail */
} __packed;
@@ -52,12 +52,10 @@ struct mt7996_mcu_thermal_enable {
u8 rsv[2];
} __packed;
-struct mt7996_mcu_csa_notify {
- struct mt7996_mcu_rxd rxd;
-
+struct mt7996_mcu_countdown_notify {
u8 omac_idx;
- u8 csa_count;
- u8 band_idx;
+ u8 count;
+ u8 csa_failure_reason; /* 0: success, 1: beacon disabled */
u8 rsv;
} __packed;
@@ -147,7 +145,7 @@ struct mt7996_mcu_background_chain_ctrl {
u8 rsv[2];
} __packed;
-struct mt7996_mcu_eeprom {
+struct mt7996_mcu_eeprom_update {
u8 _rsv[4];
__le16 tag;
@@ -157,6 +155,43 @@ struct mt7996_mcu_eeprom {
__le16 buf_len;
} __packed;
+union eeprom_data {
+ struct {
+ __le32 data_len;
+ DECLARE_FLEX_ARRAY(u8, data);
+ } ext_eeprom;
+ DECLARE_FLEX_ARRAY(u8, efuse);
+} __packed;
+
+struct mt7996_mcu_eeprom_info {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 addr;
+ __le32 valid;
+} __packed;
+
+struct mt7996_mcu_eeprom_access {
+ struct mt7996_mcu_eeprom_info info;
+ union eeprom_data eeprom;
+} __packed;
+
+struct mt7996_mcu_eeprom_access_event {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 version;
+ __le32 addr;
+ __le32 valid;
+ __le32 size;
+ __le32 magic_no;
+ __le32 type;
+ __le32 rsv[4];
+ union eeprom_data eeprom;
+} __packed;
+
struct mt7996_mcu_phy_rx_info {
u8 category;
u8 rate;
@@ -414,7 +449,16 @@ struct bss_bcn_cntdwn_tlv {
__le16 tag;
__le16 len;
u8 cnt;
- u8 rsv[3];
+ union {
+ struct {
+ bool static_pp;
+ bool abort;
+ } csa;
+ struct {
+ bool abort;
+ } cca;
+ };
+ u8 rsv;
} __packed;
struct bss_bcn_mbss_tlv {
@@ -474,6 +518,24 @@ struct bss_mld_tlv {
u8 __rsv[2];
} __packed;
+struct bss_prot_tlv {
+ __le16 tag;
+ __le16 len;
+ __le32 prot_mode;
+} __packed;
+
+struct bss_mld_link_op_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 group_mld_id;
+ u8 own_mld_id;
+ u8 mac_addr[ETH_ALEN];
+ u8 remap_idx;
+ u8 link_operation;
+ u8 link_id;
+ u8 rsv[2];
+} __packed;
+
struct sta_rec_ht_uni {
__le16 tag;
__le16 len;
@@ -647,6 +709,28 @@ struct mld_setup_link {
u8 __rsv;
} __packed;
+struct mld_req_hdr {
+ u8 ver;
+ u8 mld_addr[ETH_ALEN];
+ u8 mld_idx;
+ u8 flag;
+ u8 rsv[3];
+ u8 buf[];
+} __packed;
+
+struct mld_reconf_stop_link {
+ __le16 tag;
+ __le16 len;
+ __le16 link_bitmap;
+ u8 rsv[2];
+ u8 bss_idx[16];
+} __packed;
+
+enum {
+ UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03,
+ UNI_CMD_MLD_RECONF_STOP_LINK = 0x04,
+};
+
struct hdr_trans_en {
__le16 tag;
__le16 len;
@@ -791,6 +875,11 @@ enum {
UNI_CHANNEL_RX_PATH,
};
+enum {
+ UNI_CHIP_CONFIG_NIC_CAPA = 3,
+ UNI_CHIP_CONFIG_DUP_WTBL = 4,
+};
+
#define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \
sizeof(struct mt76_connac_bss_basic_tlv) + \
sizeof(struct bss_rlm_tlv) + \
@@ -837,6 +926,7 @@ enum {
enum {
UNI_BAND_CONFIG_RADIO_ENABLE,
UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+ UNI_BAND_CONFIG_MAC_ENABLE_CTRL = 0x0c,
};
enum {
@@ -857,6 +947,10 @@ enum {
};
enum {
+ UNI_EXT_EEPROM_ACCESS = 1,
+};
+
+enum {
UNI_VOW_DRR_CTRL,
UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index 7a884311800e..bdcf72457954 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -29,6 +29,10 @@
#define MT7996_RX_RING_SIZE 1536
#define MT7996_RX_MCU_RING_SIZE 512
#define MT7996_RX_MCU_RING_SIZE_WA 1024
+#define MT7996_NPU_TX_RING_SIZE 1024
+#define MT7996_NPU_RX_RING_SIZE 1024
+#define MT7996_NPU_TXD_SIZE 3
+
/* scatter-gather of mcu event is not supported in connac3 */
#define MT7996_RX_MCU_BUF_SIZE (2048 + \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
@@ -60,6 +64,11 @@
#define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
#define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
+#define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
+#define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
+#define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
+#define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
+
#define MT7990_FIRMWARE_WA ""
#define MT7990_FIRMWARE_WM "mediatek/mt7996/mt7990_wm.bin"
#define MT7990_FIRMWARE_DSP ""
@@ -75,12 +84,14 @@
#define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin"
#define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23.bin"
#define MT7992_EEPROM_DEFAULT_23_INT "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin"
+#define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin"
#define MT7990_EEPROM_DEFAULT "mediatek/mt7996/mt7990_eeprom.bin"
#define MT7990_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7990_eeprom_2i5i.bin"
#define MT7996_EEPROM_SIZE 7680
#define MT7996_EEPROM_BLOCK_SIZE 16
+#define MT7996_EXT_EEPROM_BLOCK_SIZE 1024
#define MT7996_TOKEN_SIZE 16384
#define MT7996_HW_TOKEN_SIZE 8192
@@ -153,6 +164,7 @@ enum mt7996_var_type {
enum mt7992_var_type {
MT7992_VAR_TYPE_44,
MT7992_VAR_TYPE_23,
+ MT7992_VAR_TYPE_24,
};
enum mt7990_var_type {
@@ -165,6 +177,18 @@ enum mt7996_fem_type {
MT7996_FEM_MIX,
};
+enum mt7996_eeprom_mode {
+ EEPROM_MODE_DEFAULT_BIN,
+ EEPROM_MODE_EFUSE,
+ EEPROM_MODE_FLASH,
+ EEPROM_MODE_EXT,
+};
+
+#define MT7996_EFUSE_BASE_OFFS_ADIE0 0x400
+#define MT7996_EFUSE_BASE_OFFS_ADIE1 0x1e00
+#define MT7996_EFUSE_BASE_OFFS_ADIE2 0x1200
+#define MT7992_EFUSE_BASE_OFFS_ADIE1 0x1200
+
enum mt7996_txq_id {
MT7996_TXQ_FWDL = 16,
MT7996_TXQ_MCU_WM,
@@ -252,8 +276,6 @@ struct mt7996_vif_link {
struct mt76_vif_link mt76; /* must be first */
struct mt7996_sta_link msta_link;
- struct mt7996_phy *phy;
-
struct cfg80211_bitrate_mask bitrate_mask;
u8 mld_idx;
@@ -377,6 +399,7 @@ struct mt7996_phy {
bool has_aux_rx;
bool counter_reset;
+ bool rdd_tx_paused;
};
struct mt7996_dev {
@@ -436,7 +459,7 @@ struct mt7996_dev {
u32 hw_pattern;
- bool flash_mode:1;
+ u8 eeprom_mode;
bool has_eht:1;
struct {
@@ -473,6 +496,8 @@ struct mt7996_dev {
struct list_head page_map[MT7996_RRO_MSDU_PG_HASH_SIZE];
} wed_rro;
+ dma_addr_t npu_txd_addr[2 * MT7996_NPU_TXD_SIZE];
+
bool ibf;
u8 fw_debug_wm;
u8 fw_debug_wa;
@@ -669,6 +694,8 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct mt76_vif_link *mlink,
struct mt7996_sta_link *msta_link, int enable);
+int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy,
+ struct mt7996_vif_link *link);
int mt7996_mcu_add_sta(struct mt7996_dev *dev,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta,
@@ -678,6 +705,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev,
int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link);
+void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta);
int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
struct ieee80211_ampdu_params *params,
struct ieee80211_vif *vif, bool enable);
@@ -707,8 +735,9 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
void *data, u8 link_id, u32 field);
int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len);
-int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len,
+ enum mt7996_eeprom_mode mode);
+int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num);
int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action);
@@ -719,6 +748,8 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
const struct mt7996_dfs_pattern *pattern);
int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link,
+ u8 ht_mode, bool use_cts_prot);
int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
@@ -726,6 +757,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy);
+int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy);
int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val);
int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
struct cfg80211_chan_def *chandef);
@@ -743,6 +775,13 @@ void mt7996_mcu_exit(struct mt7996_dev *dev);
int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled);
+int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev);
+int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ u16 removed_links);
+int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev,
+ struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link, bool add);
static inline bool mt7996_has_hwrro(struct mt7996_dev *dev)
{
@@ -802,6 +841,11 @@ static inline bool mt7996_has_wa(struct mt7996_dev *dev)
return !is_mt7990(&dev->mt76);
}
+static inline bool mt7996_has_ext_eeprom(struct mt7996_dev *dev)
+{
+ return !is_mt7996(&dev->mt76);
+}
+
void mt7996_mac_init(struct mt7996_dev *dev);
u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw);
bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask);
@@ -823,8 +867,9 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link,
u8 flowid);
-void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
- struct mt7996_sta_link *msta_link);
+void mt7996_mac_sta_remove_link(struct mt7996_dev *dev,
+ struct ieee80211_sta *sta,
+ unsigned int link_id, bool flush);
void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
struct ieee80211_twt_setup *twt);
@@ -861,6 +906,10 @@ int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
struct mt7996_vif_link *link,
struct mt7996_sta_link *msta_link);
int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
+int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_eml_params *eml_params);
#ifdef CONFIG_MAC80211_DEBUGFS
void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
@@ -877,12 +926,19 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
#endif
int mt7996_dma_rro_init(struct mt7996_dev *dev);
+void mt7996_dma_rro_start(struct mt7996_dev *dev);
#ifdef CONFIG_MT7996_NPU
+int __mt7996_npu_hw_init(struct mt7996_dev *dev);
int mt7996_npu_hw_init(struct mt7996_dev *dev);
int mt7996_npu_hw_stop(struct mt7996_dev *dev);
int mt7996_npu_rx_queues_init(struct mt7996_dev *dev);
#else
+static inline int __mt7996_npu_hw_init(struct mt7996_dev *dev)
+{
+ return 0;
+}
+
static inline int mt7996_npu_hw_init(struct mt7996_dev *dev)
{
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
index 29bb735da4cb..b8006b8729a1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
@@ -8,55 +8,20 @@
#include "mt7996.h"
-static int mt7996_npu_offload_init(struct mt7996_dev *dev,
- struct airoha_npu *npu)
+static int mt7992_npu_txrx_offload_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
{
+ u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0;
phys_addr_t phy_addr = dev->mt76.mmio.phy_addr;
- u32 val, hif1_ofs = 0, dma_addr;
+ u32 dma_addr;
int i, err;
- err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION,
- &val, GFP_KERNEL);
- if (err) {
- dev_warn(dev->mt76.dev, "failed getting NPU fw version\n");
- return err;
- }
-
- dev_info(dev->mt76.dev, "NPU version: %0d.%d\n",
- (val >> 16) & 0xffff, val & 0xffff);
-
- err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE,
- dev->mt76.mmio.npu_type, GFP_KERNEL);
- if (err) {
- dev_warn(dev->mt76.dev,
- "failed setting NPU wlan PCIe port type\n");
- return err;
- }
-
- if (dev->hif2)
- hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
-
for (i = MT_BAND0; i < MT_BAND2; i++) {
- dma_addr = phy_addr;
- if (i)
- dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90 +
- hif1_ofs;
- else
- dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80;
-
- err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
- dma_addr, GFP_KERNEL);
- if (err) {
- dev_warn(dev->mt76.dev,
- "failed setting NPU wlan PCIe desc addr\n");
- return err;
- }
-
err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_DESC,
MT7996_RX_RING_SIZE, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed setting NPU wlan PCIe desc size\n");
+ "failed setting NPU wlan rx desc size\n");
return err;
}
@@ -97,10 +62,173 @@ static int mt7996_npu_offload_init(struct mt7996_dev *dev,
phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed setting NPU wlan rro_ack_sn desc addr\n");
+ "failed setting NPU wlan tx desc addr\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int mt7996_npu_txrx_offload_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0;
+ phys_addr_t phy_addr = dev->mt76.mmio.phy_addr;
+ u32 dma_addr;
+ int err;
+
+ /* npu rx rro ring0 */
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ /* npu rx rro ring1 */
+ err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_NPU_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ /* msdu pg 2GHz */
+ dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0) + 0xa0;
+ err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_NPU_RX_RING_SIZE / 4, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ /* msdu pg 5GHz */
+ dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1) + 0xb0;
+ err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_NPU_RX_RING_SIZE / 2, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ /* msdu pg 6GHz */
+ dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2) + 0xc0;
+ err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_NPU_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ /* ind cmd ring */
+ err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ phy_addr + MT_RXQ_RRO_IND_RING_BASE,
+ GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx desc size\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 3, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR,
+ phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx desc addr\n");
+ return err;
+ }
+
+ /* npu tx */
+ dma_addr = phy_addr + MT_TXQ_RING_BASE(1) + 0x120;
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx desc addr\n");
+ return err;
+ }
+
+ dma_addr = phy_addr + MT_TXQ_RING_BASE(0) + 0x150 + hif1_ofs;
+ err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx desc addr\n");
return err;
}
+ return 0;
+}
+
+static int mt7996_npu_offload_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ u32 val;
+ int err;
+
+ err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev, "failed getting NPU fw version\n");
+ return err;
+ }
+
+ dev_info(dev->mt76.dev, "NPU version: %0d.%d\n",
+ (val >> 16) & 0xffff, val & 0xffff);
+
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE,
+ dev->mt76.mmio.npu_type, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe port type\n");
+ return err;
+ }
+
+ if (is_mt7996(&dev->mt76))
+ err = mt7996_npu_txrx_offload_init(dev, npu);
+ else
+ err = mt7992_npu_txrx_offload_init(dev, npu);
+
+ if (err)
+ return err;
+
err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE,
MT7996_HW_TOKEN_SIZE, GFP_KERNEL);
if (err)
@@ -111,7 +239,7 @@ static int mt7996_npu_offload_init(struct mt7996_dev *dev,
return 0;
}
-static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
+static int mt7992_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
{
u32 val;
int err;
@@ -120,7 +248,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan rx ring0 addr\n");
+ "failed retrieving NPU wlan rx ring0 addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base);
@@ -129,7 +257,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan rx ring1 addr\n");
+ "failed retrieving NPU wlan rx ring1 addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1].regs->desc_base);
@@ -138,7 +266,7 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan rxdmad_c ring addr\n");
+ "failed retrieving NPU wlan rxdmad_c ring addr\n");
return err;
}
writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_RXDMAD_C].regs->desc_base);
@@ -146,59 +274,121 @@ static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
return 0;
}
+static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
+{
+ u32 val;
+ int err;
+
+ err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan rx ring0 addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base);
+
+ err = mt76_npu_get_msg(npu, 2, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan rx ring2 addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2].regs->desc_base);
+
+ /* msdu pg ring */
+ err = mt76_npu_get_msg(npu, 10, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan msdu pg ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].regs->desc_base);
+
+ err = mt76_npu_get_msg(npu, 11, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan msdu pg ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].regs->desc_base);
+
+ err = mt76_npu_get_msg(npu, 12, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan msdu pg ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].regs->desc_base);
+
+ /* ind_cmd ring */
+ err = mt76_npu_get_msg(npu, 8, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan ind_cmd ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_IND].regs->desc_base);
+
+ return 0;
+}
+
static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
{
- int i, err;
+ const enum mt76_band_id band_list[] = {
+ MT_BAND0,
+ is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND1,
+ };
+ int i, index = 0;
- for (i = MT_BAND0; i < MT_BAND2; i++) {
- dma_addr_t dma_addr;
+ BUILD_BUG_ON(ARRAY_SIZE(band_list) * 3 !=
+ ARRAY_SIZE(dev->npu_txd_addr));
+
+ for (i = 0; i < ARRAY_SIZE(band_list); i++) {
+ int err, band = band_list[i], phy_id;
u32 val;
- err = mt76_npu_get_msg(npu, i + 5,
+ err = mt76_npu_get_msg(npu, band + 5,
WLAN_FUNC_GET_WAIT_RXDESC_BASE,
&val, GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
- "failed retriving NPU wlan tx ring addr\n");
+ "failed retrieving NPU wlan tx ring addr\n");
return err;
}
- writel(val, &dev->mt76.phys[i]->q_tx[0]->regs->desc_base);
- if (!dmam_alloc_coherent(dev->mt76.dma_dev,
- 256 * MT7996_TX_RING_SIZE,
- &dma_addr, GFP_KERNEL))
- return -ENOMEM;
+ phy_id = is_mt7996(&dev->mt76) ? band == MT_BAND0 ? 1 : 0
+ : band;
+ writel(val, &dev->mt76.phys[phy_id]->q_tx[0]->regs->desc_base);
- err = mt76_npu_send_msg(npu, i,
+ err = mt76_npu_send_msg(npu, band,
WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
- dma_addr, GFP_KERNEL);
+ dev->npu_txd_addr[index++], GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
"failed setting NPU wlan queue buf addr\n");
return err;
}
- if (!dmam_alloc_coherent(dev->mt76.dma_dev,
- 256 * MT7996_TX_RING_SIZE,
- &dma_addr, GFP_KERNEL))
- return -ENOMEM;
-
- err = mt76_npu_send_msg(npu, i + 5,
+ err = mt76_npu_send_msg(npu, band + 5,
WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
- dma_addr, GFP_KERNEL);
+ dev->npu_txd_addr[index++],
+ GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
"failed setting NPU wlan tx buf addr\n");
return err;
}
- if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024,
- &dma_addr, GFP_KERNEL))
- return -ENOMEM;
-
- err = mt76_npu_send_msg(npu, i + 10,
+ err = mt76_npu_send_msg(npu, band + 10,
WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
- dma_addr, GFP_KERNEL);
+ dev->npu_txd_addr[index++],
+ GFP_KERNEL);
if (err) {
dev_warn(dev->mt76.dev,
"failed setting NPU wlan tx buf base\n");
@@ -212,8 +402,9 @@ static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
static int mt7996_npu_rx_event_init(struct mt7996_dev *dev,
struct airoha_npu *npu)
{
- struct mt76_queue *q = &dev->mt76.q_rx[MT_RXQ_MAIN_WA];
+ int qid = is_mt7996(&dev->mt76) ? MT_RXQ_TXFREE_BAND0 : MT_RXQ_MAIN_WA;
phys_addr_t phy_addr = dev->mt76.mmio.phy_addr;
+ struct mt76_queue *q = &dev->mt76.q_rx[qid];
int err;
err = mt76_npu_send_msg(npu, 0,
@@ -233,7 +424,8 @@ static int mt7996_npu_rx_event_init(struct mt7996_dev *dev,
return err;
}
- phy_addr += MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA) + 0x20;
+ phy_addr += MT_RXQ_RING_BASE(qid);
+ phy_addr += is_mt7996(&dev->mt76) ? 0x90 : 0x20;
err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
phy_addr, GFP_KERNEL);
if (err)
@@ -242,11 +434,54 @@ static int mt7996_npu_rx_event_init(struct mt7996_dev *dev,
return err;
}
+static int mt7996_npu_set_pcie_addr(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0;
+ dma_addr_t dma_addr = dev->mt76.mmio.phy_addr;
+ int err;
+
+ dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80;
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ dma_addr = dev->mt76.mmio.phy_addr + hif1_ofs;
+ if (is_mt7996(&dev->mt76)) {
+ dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + 0x60;
+ err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ } else {
+ dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90;
+ err = mt76_npu_send_msg(npu, 1, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ }
+
+ if (err)
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+
+ return err;
+}
+
static int mt7996_npu_tx_done_init(struct mt7996_dev *dev,
struct airoha_npu *npu)
{
int err;
+ /* rro ring cpu idx */
+ err = mt76_npu_send_msg(npu, 15, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ 0, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
0, GFP_KERNEL);
if (err) {
@@ -278,40 +513,79 @@ int mt7996_npu_rx_queues_init(struct mt7996_dev *dev)
&dev->mt76.q_rx[MT_RXQ_NPU1]);
}
-int mt7996_npu_hw_init(struct mt7996_dev *dev)
+int __mt7996_npu_hw_init(struct mt7996_dev *dev)
{
struct airoha_npu *npu;
- int i, err = 0;
-
- mutex_lock(&dev->mt76.mutex);
+ int i, err;
npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex);
if (!npu)
- goto unlock;
+ return 0;
err = mt7996_npu_offload_init(dev, npu);
if (err)
- goto unlock;
+ return err;
+
+ if (is_mt7996(&dev->mt76))
+ err = mt7996_npu_rxd_init(dev, npu);
+ else
+ err = mt7992_npu_rxd_init(dev, npu);
- err = mt7996_npu_rxd_init(dev, npu);
if (err)
- goto unlock;
+ return err;
err = mt7996_npu_txd_init(dev, npu);
if (err)
- goto unlock;
+ return err;
err = mt7996_npu_rx_event_init(dev, npu);
if (err)
- goto unlock;
+ return err;
+
+ err = mt7996_npu_set_pcie_addr(dev, npu);
+ if (err)
+ return err;
err = mt7996_npu_tx_done_init(dev, npu);
if (err)
- goto unlock;
+ return err;
for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++)
airoha_npu_wlan_enable_irq(npu, i - MT_RXQ_NPU0);
-unlock:
+
+ return 0;
+}
+
+int mt7996_npu_hw_init(struct mt7996_dev *dev)
+{
+ int i, err;
+
+ BUILD_BUG_ON(ARRAY_SIZE(dev->npu_txd_addr) % 3);
+
+ for (i = 0; i < ARRAY_SIZE(dev->npu_txd_addr); i += 3) {
+ int band = i && is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND0;
+ u32 size = is_mt7996(&dev->mt76) ? band == MT_BAND2
+ ? MT7996_NPU_TX_RING_SIZE
+ : MT7996_NPU_RX_RING_SIZE / 2
+ : MT7996_TX_RING_SIZE;
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size,
+ &dev->npu_txd_addr[i], GFP_KERNEL))
+ return -ENOMEM;
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size,
+ &dev->npu_txd_addr[i + 1],
+ GFP_KERNEL))
+ return -ENOMEM;
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024,
+ &dev->npu_txd_addr[i + 2],
+ GFP_KERNEL))
+ return -ENOMEM;
+ }
+
+ mutex_lock(&dev->mt76.mutex);
+ err = __mt7996_npu_hw_init(dev);
mutex_unlock(&dev->mt76.mutex);
return err;
@@ -320,33 +594,38 @@ unlock:
int mt7996_npu_hw_stop(struct mt7996_dev *dev)
{
struct airoha_npu *npu;
- int i, err;
+ int i, err = 0;
u32 info;
+ mutex_lock(&dev->mt76.mutex);
+
npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex);
if (!npu)
- return 0;
+ goto unlock;
err = mt76_npu_send_msg(npu, 4, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
0, GFP_KERNEL);
if (err)
- return err;
+ goto unlock;
for (i = 0; i < 10; i++) {
err = mt76_npu_get_msg(npu, 3, WLAN_FUNC_GET_WAIT_NPU_INFO,
&info, GFP_KERNEL);
- if (err)
- continue;
+ if (!err && !info)
+ break;
- if (info) {
- err = -ETIMEDOUT;
- continue;
- }
+ err = -ETIMEDOUT;
+ usleep_range(10000, 15000);
}
if (!err)
err = mt76_npu_send_msg(npu, 6,
WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
0, GFP_KERNEL);
+ else
+ dev_err(dev->mt76.dev, "npu stop failed\n");
+unlock:
+ mutex_unlock(&dev->mt76.mutex);
+
return err;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
index e48e0e575b64..c6379933b6c3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
@@ -159,6 +159,9 @@ enum offs_rev {
#define MT_MDP_BASE 0x820cc000
#define MT_MDP(ofs) (MT_MDP_BASE + (ofs))
+#define MT_MDP_DCR0 MT_MDP(0x800)
+#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19)
+
#define MT_MDP_DCR2 MT_MDP(0x8e8)
#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2)
@@ -733,7 +736,15 @@ enum offs_rev {
#define MT_HW_REV 0x70010204
#define MT_HW_REV1 0x8a00
+#define MT_WF_L05_RST 0x70028550
+#define MT_WF_L05_RST_WF_RST_MASK GENMASK(4, 0)
+
#define MT_WF_SUBSYS_RST 0x70028600
+#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST BIT(0)
+#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT BIT(5)
+#define MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT BIT(6)
+#define MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT BIT(16)
+#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE GENMASK(15, 8)
/* PCIE MAC */
#define MT_PCIE_MAC_BASE 0x74030000
diff --git a/drivers/net/wireless/mediatek/mt76/npu.c b/drivers/net/wireless/mediatek/mt76/npu.c
index ec36975f6dc9..c4c7c0af6321 100644
--- a/drivers/net/wireless/mediatek/mt76/npu.c
+++ b/drivers/net/wireless/mediatek/mt76/npu.c
@@ -390,6 +390,36 @@ int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
EXPORT_SYMBOL_GPL(mt76_npu_net_setup_tc);
+int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex,
+ u32 direction, u32 i_count_addr,
+ u32 o_status_addr, u32 o_count_addr)
+{
+ struct {
+ __le32 dir;
+ __le32 in_count_addr;
+ __le32 out_status_addr;
+ __le32 out_count_addr;
+ } info = {
+ .dir = cpu_to_le32(direction),
+ .in_count_addr = cpu_to_le32(i_count_addr),
+ .out_status_addr = cpu_to_le32(o_status_addr),
+ .out_count_addr = cpu_to_le32(o_count_addr),
+ };
+ struct airoha_npu *npu;
+ int err = -ENODEV;
+
+ rcu_read_lock();
+ npu = rcu_dereference(dev->mmio.npu);
+ if (npu)
+ err = airoha_npu_wlan_send_msg(npu, ifindex,
+ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
+ &info, sizeof(info), GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mt76_npu_send_txrx_addr);
+
void mt76_npu_disable_irqs(struct mt76_dev *dev)
{
struct airoha_npu *npu;
@@ -420,10 +450,6 @@ int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type)
struct airoha_npu *npu;
int err = 0;
- /* NPU offloading is only supported by MT7992 */
- if (!is_mt7992(dev))
- return 0;
-
mutex_lock(&dev->mutex);
npu = airoha_npu_get(dev->dev);
@@ -456,7 +482,8 @@ int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type)
dev->mmio.phy_addr = phy_addr;
dev->mmio.npu_type = type;
/* NPU offloading requires HW-RRO for RX packet reordering. */
- dev->hwrro_mode = MT76_HWRRO_V3_1;
+ dev->hwrro_mode = is_mt7996(dev) ? MT76_HWRRO_V3 : MT76_HWRRO_V3_1;
+ dev->rx_token_size = 32768;
rcu_assign_pointer(dev->mmio.npu, npu);
rcu_assign_pointer(dev->mmio.ppe_dev, ppe_dev);
diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c
index 63b0447e55c1..7fe1b1fbb699 100644
--- a/drivers/net/wireless/mediatek/mt76/scan.c
+++ b/drivers/net/wireless/mediatek/mt76/scan.c
@@ -16,9 +16,11 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
clear_bit(MT76_SCANNING, &phy->state);
- if (dev->scan.chan && phy->main_chandef.chan &&
- !test_bit(MT76_MCU_RESET, &dev->phy.state))
+ if (dev->scan.chan && phy->main_chandef.chan && phy->offchannel &&
+ !test_bit(MT76_MCU_RESET, &dev->phy.state)) {
mt76_set_channel(phy, &phy->main_chandef, false);
+ mt76_offchannel_notify(phy, false);
+ }
mt76_put_vif_phy_link(phy, dev->scan.vif, dev->scan.mlink);
memset(&dev->scan, 0, sizeof(dev->scan));
if (!test_bit(MT76_MCU_RESET, &dev->phy.state))
@@ -27,6 +29,10 @@ static void mt76_scan_complete(struct mt76_dev *dev, bool abort)
void mt76_abort_scan(struct mt76_dev *dev)
{
+ spin_lock_bh(&dev->scan_lock);
+ dev->scan.beacon_wait = false;
+ spin_unlock_bh(&dev->scan_lock);
+
cancel_delayed_work_sync(&dev->scan_work);
mt76_scan_complete(dev, true);
}
@@ -77,6 +83,27 @@ out:
rcu_read_unlock();
}
+void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan)
+{
+ struct mt76_phy *phy;
+
+ spin_lock(&dev->scan_lock);
+
+ if (!dev->scan.beacon_wait || dev->scan.beacon_received ||
+ dev->scan.chan != chan)
+ goto out;
+
+ phy = dev->scan.phy;
+ if (!phy)
+ goto out;
+
+ dev->scan.beacon_received = true;
+ ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0);
+
+out:
+ spin_unlock(&dev->scan_lock);
+}
+
void mt76_scan_work(struct work_struct *work)
{
struct mt76_dev *dev = container_of(work, struct mt76_dev,
@@ -85,35 +112,60 @@ void mt76_scan_work(struct work_struct *work)
struct cfg80211_chan_def chandef = {};
struct mt76_phy *phy = dev->scan.phy;
int duration = HZ / 9; /* ~110 ms */
+ bool beacon_rx, offchannel = true;
int i;
+ if (!phy || !req)
+ return;
+
+ spin_lock_bh(&dev->scan_lock);
+ beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received;
+ dev->scan.beacon_wait = false;
+ spin_unlock_bh(&dev->scan_lock);
+
+ if (beacon_rx)
+ goto probe;
+
if (dev->scan.chan_idx >= req->n_channels) {
mt76_scan_complete(dev, false);
return;
}
- if (dev->scan.chan && phy->num_sta) {
+ if (dev->scan.chan && phy->num_sta && phy->offchannel) {
dev->scan.chan = NULL;
mt76_set_channel(phy, &phy->main_chandef, false);
+ mt76_offchannel_notify(phy, false);
goto out;
}
dev->scan.chan = req->channels[dev->scan.chan_idx++];
- cfg80211_chandef_create(&chandef, dev->scan.chan, NL80211_CHAN_HT20);
- mt76_set_channel(phy, &chandef, true);
+ offchannel = mt76_offchannel_chandef(phy, dev->scan.chan, &chandef);
+
+ if (offchannel)
+ mt76_offchannel_notify(phy, true);
+ mt76_set_channel(phy, &chandef, offchannel);
- if (!req->n_ssids ||
- chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
+ if (!req->n_ssids)
goto out;
- duration = HZ / 16; /* ~60 ms */
+ if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) {
+ spin_lock_bh(&dev->scan_lock);
+ dev->scan.beacon_received = false;
+ dev->scan.beacon_wait = true;
+ spin_unlock_bh(&dev->scan_lock);
+ goto out;
+ }
+
+probe:
+ if (phy->offchannel)
+ duration = HZ / 16; /* ~60 ms */
local_bh_disable();
for (i = 0; i < req->n_ssids; i++)
mt76_scan_send_probe(dev, &req->ssids[i]);
local_bh_enable();
out:
- if (dev->scan.chan)
+ if (dev->scan.chan && phy->offchannel)
duration = max_t(int, duration,
msecs_to_jiffies(req->duration +
(req->duration >> 5)));
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 9ec6d0b53a84..22f9690634c9 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -227,7 +227,9 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sta *sta;
int pending;
+ int i;
if (!wcid || info->tx_time_est)
return;
@@ -235,6 +237,17 @@ mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid,
pending = atomic_dec_return(&wcid->non_aql_packets);
if (pending < 0)
atomic_cmpxchg(&wcid->non_aql_packets, pending, 0);
+
+ sta = wcid_to_sta(wcid);
+ if (!sta || pending != MT_MAX_NON_AQL_PKT - 1)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
+ if (!sta->txq[i])
+ continue;
+
+ ieee80211_schedule_txq(dev->hw, sta->txq[i]);
+ }
}
void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb,
@@ -542,6 +555,9 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid)
if (!wcid || test_bit(MT_WCID_FLAG_PS, &wcid->flags))
continue;
+ if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT)
+ continue;
+
phy = mt76_dev_phy(dev, wcid->phy_idx);
if (test_bit(MT76_RESET, &phy->state) || phy->offchannel)
continue;
@@ -616,7 +632,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,
if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) &&
!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
- !ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_is_data_present(hdr->frame_control) &&
(!ieee80211_is_bufferable_mmpdu(skb) ||
ieee80211_is_deauth(hdr->frame_control) ||
head == &wcid->tx_offchannel))
@@ -644,7 +660,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,
return ret;
}
-static void mt76_txq_schedule_pending(struct mt76_phy *phy)
+void mt76_txq_schedule_pending(struct mt76_phy *phy)
{
LIST_HEAD(tx_list);
int ret = 0;
@@ -850,9 +866,15 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
token = idr_alloc(&dev->token, *ptxwi, dev->token_start,
dev->token_start + dev->token_size,
GFP_ATOMIC);
- if (token >= dev->token_start)
+ if (token >= dev->token_start) {
dev->token_count++;
+ if ((*ptxwi)->qid == MT_TXQ_PSD) {
+ struct mt76_phy *mphy = mt76_dev_phy(dev, (*ptxwi)->phy_idx);
+ atomic_inc(&mphy->mgmt_tx_pending);
+ }
+ }
+
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
if (mtk_wed_device_active(&dev->mmio.wed) &&
token >= dev->mmio.wed.wlan.token_start)
@@ -897,6 +919,12 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
if (txwi) {
dev->token_count--;
+ if (txwi->qid == MT_TXQ_PSD) {
+ struct mt76_phy *mphy = mt76_dev_phy(dev, txwi->phy_idx);
+ if (atomic_dec_and_test(&mphy->mgmt_tx_pending))
+ wake_up(&dev->tx_wait);
+ }
+
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
if (mtk_wed_device_active(&dev->mmio.wed) &&
token >= dev->mmio.wed.wlan.token_start &&
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
index 1b5cc271a9e1..bad6ca821400 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mcu.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c
@@ -403,12 +403,18 @@ error:
return ret;
}
+static const char * const mt7601u_fw_paths[] = {
+ "mediatek/" MT7601U_FIRMWARE,
+ MT7601U_FIRMWARE,
+};
+
static int mt7601u_load_firmware(struct mt7601u_dev *dev)
{
const struct firmware *fw;
const struct mt76_fw_header *hdr;
int len, ret;
u32 val;
+ int i;
mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN |
MT_USB_DMA_CFG_TX_BULK_EN));
@@ -416,7 +422,14 @@ static int mt7601u_load_firmware(struct mt7601u_dev *dev)
if (firmware_running(dev))
return firmware_request_cache(dev->dev, MT7601U_FIRMWARE);
- ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev);
+ /* Try loading firmware from multiple locations */
+ fw = NULL;
+ for (i = 0; i < MT7601U_FIRMWARE_PATHS; i++) {
+ ret = request_firmware(&fw, mt7601u_fw_paths[i], dev->dev);
+ if (ret == 0)
+ break;
+ }
+
if (ret)
return ret;
diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h
index 9fdf35970339..723025f84483 100644
--- a/drivers/net/wireless/mediatek/mt7601u/usb.h
+++ b/drivers/net/wireless/mediatek/mt7601u/usb.h
@@ -9,6 +9,7 @@
#include "mt7601u.h"
#define MT7601U_FIRMWARE "mt7601u.bin"
+#define MT7601U_FIRMWARE_PATHS ARRAY_SIZE(mt7601u_fw_paths)
#define MT_VEND_REQ_MAX_RETRY 10
#define MT_VEND_REQ_TOUT_MS 300
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 4a1eee1671b9..1fcf5d0d2e13 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -4640,6 +4640,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
{
.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -4748,6 +4753,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
#ifdef CONFIG_MAC80211_MESH
{
@@ -4917,6 +4927,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
{
.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -5042,6 +5057,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
#ifdef CONFIG_MAC80211_MESH
{
@@ -5235,6 +5255,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
{
.types_mask = BIT(NL80211_IFTYPE_AP) |
@@ -5381,6 +5406,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
#ifdef CONFIG_MAC80211_MESH
{
@@ -5472,6 +5502,11 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
/* PPE threshold information is not supported */
},
+ .uhr_cap = {
+ .has_uhr = true,
+ .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
+ IEEE80211_UHR_PHY_CAP_ELR_TX,
+ },
},
#endif
};
diff --git a/include/linux/ieee80211-nan.h b/include/linux/ieee80211-nan.h
index d07959bf8a90..ebf28ea651f9 100644
--- a/include/linux/ieee80211-nan.h
+++ b/include/linux/ieee80211-nan.h
@@ -9,7 +9,7 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (c) 2018 - 2025 Intel Corporation
+ * Copyright (c) 2018 - 2026 Intel Corporation
*/
#ifndef LINUX_IEEE80211_NAN_H
@@ -23,6 +23,11 @@
#define NAN_OP_MODE_160MHZ 0x04
#define NAN_OP_MODE_PNDL_SUPPRTED 0x08
+#define NAN_DEV_CAPA_NUM_TX_ANT_POS 0
+#define NAN_DEV_CAPA_NUM_TX_ANT_MASK 0x0f
+#define NAN_DEV_CAPA_NUM_RX_ANT_POS 4
+#define NAN_DEV_CAPA_NUM_RX_ANT_MASK 0xf0
+
/* NAN Device capabilities, as defined in Wi-Fi Aware (TM) specification
* Table 79
*/
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 52db36120314..b5d649db123f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1194,6 +1194,13 @@ struct ieee80211_mgmt {
#define IEEE80211_MIN_ACTION_SIZE(type) offsetofend(struct ieee80211_mgmt, u.action.type)
+/* Link Reconfiguration Status Duple field */
+struct ieee80211_ml_reconf_status {
+ u8 info;
+ __le16 status;
+} __packed;
+
+#define IEEE80211_ML_RECONF_LINK_ID_MASK 0xf
/* Management MIC information element (IEEE 802.11w) for CMAC */
struct ieee80211_mmie {
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 8cd870ece351..9d3639ff9c28 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1831,6 +1831,7 @@ struct cfg80211_ttlm_params {
* @eml_cap: EML capabilities of this station
* @link_sta_params: link related params.
* @epp_peer: EPP peer indication
+ * @nmi_mac: MAC address of the NMI station of the NAN peer
*/
struct station_parameters {
struct net_device *vlan;
@@ -1858,6 +1859,7 @@ struct station_parameters {
u16 eml_cap;
struct link_station_parameters link_sta_params;
bool epp_peer;
+ const u8 *nmi_mac;
};
/**
@@ -1897,6 +1899,8 @@ struct station_del_parameters {
* entry that is operating, has been marked authorized by userspace)
* @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed)
* @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed)
+ * @CFG80211_STA_NAN_MGMT: NAN management interface station
+ * @CFG80211_STA_NAN_DATA: NAN data path station
*/
enum cfg80211_station_type {
CFG80211_STA_AP_CLIENT,
@@ -1908,6 +1912,8 @@ enum cfg80211_station_type {
CFG80211_STA_TDLS_PEER_ACTIVE,
CFG80211_STA_MESH_PEER_KERNEL,
CFG80211_STA_MESH_PEER_USER,
+ CFG80211_STA_NAN_MGMT,
+ CFG80211_STA_NAN_DATA,
};
/**
@@ -3981,6 +3987,77 @@ struct cfg80211_qos_map {
};
/**
+ * DOC: Neighbor Awareness Networking (NAN)
+ *
+ * NAN uses two interface types:
+ *
+ * - %NL80211_IFTYPE_NAN: a non-netdev interface. This has two roles: (1) holds
+ * the configuration of all NAN activities (DE parameters, synchronisation
+ * parameters, local schedule, etc.), and (2) uses as the NAN Management
+ * Interface (NMI), which is used for NAN management communication.
+ *
+ * - %NL80211_IFTYPE_NAN_DATA: The NAN Data Interface (NDI), used for data
+ * communication with NAN peers.
+ *
+ * An NDI interface can only be started (IFF_UP) if the NMI one is running and
+ * NAN is started. Before NAN is stopped, all associated NDI interfaces
+ * must be stopped first.
+ *
+ * The local schedule specifies which channels the device is available on and
+ * when. Must be cancelled before NAN is stopped.
+ *
+ * NAN Stations
+ * ~~~~~~~~~~~~
+ *
+ * There are two types of stations corresponding to the two interface types:
+ *
+ * - NMI station: Represents the NAN peer. Peer-specific data such as the peer's
+ * schedule and the HT, VHT and HE capabilities belongs to the NMI station.
+ * Also used for Tx/Rx of NAN management frames to/from the peer.
+ * Added on the %NL80211_IFTYPE_NAN interface.
+ *
+ * - NDI station: Used for Tx/Rx of data frames (and non-NAN management frames)
+ * for a specific NDP established with the NAN peer. Added on the
+ * %NL80211_IFTYPE_NAN_DATA interface.
+ *
+ * A peer may reuse its NMI address as the NDI address. In that case, two
+ * separate stations should be added even though they share the same MAC
+ * address.
+ *
+ * HT, VHT and HE capabilities should not changes after it was set. It is the
+ * driver's responsibility to check that.
+ *
+ * An NDI station can only be added if the corresponding NMI station has already
+ * been configured with HT (and possibly VHT and HE) capabilities. It is the
+ * driver's responsibility to check that.
+ *
+ * All NDI stations must be removed before corresponding NMI station is removed.
+ * Therefore, removing a NMI station implies that the associated NDI station(s)
+ * (if any) will be removed first.
+ *
+ * NAN Dependencies
+ * ~~~~~~~~~~~~~~~~
+ *
+ * The following diagram shows the dependencies between NAN components.
+ * An arrow from A to B means A must be started/added before B, and B must be
+ * stopped/removed before A:
+ *
+ * +-------------+
+ * | NMI iface |---(local schedule)
+ * +------+------+
+ * / \
+ * v v
+ * +-----------+ +-------------+
+ * | NDI iface | | NMI sta |---(peer schedule)
+ * +-----+-----+ +------+------+
+ * \ /
+ * v v
+ * +----------+
+ * | NDI sta |
+ * +----------+
+ */
+
+/**
* struct cfg80211_nan_band_config - NAN band specific configuration
*
* @chan: Pointer to the IEEE 802.11 channel structure. The channel to be used
@@ -4050,6 +4127,102 @@ struct cfg80211_nan_conf {
u16 vendor_elems_len;
};
+#define CFG80211_NAN_SCHED_NUM_TIME_SLOTS 32
+
+/**
+ * struct cfg80211_nan_channel - NAN channel configuration
+ *
+ * This struct defines a NAN channel configuration
+ *
+ * @chandef: the channel definition
+ * @channel_entry: pointer to the Channel Entry blob as defined in Wi-Fi Aware
+ * (TM) 4.0 specification Table 100 (Channel Entry format for the NAN
+ * Availability attribute).
+ * @rx_nss: number of spatial streams supported on this channel
+ */
+struct cfg80211_nan_channel {
+ struct cfg80211_chan_def chandef;
+ const u8 *channel_entry;
+ u8 rx_nss;
+};
+
+/**
+ * struct cfg80211_nan_local_sched - NAN local schedule
+ *
+ * This struct defines NAN local schedule parameters
+ *
+ * @schedule: a mapping of time slots to chandef indexes in %nan_channels.
+ * An unscheduled slot will be set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT.
+ * @n_channels: number of channel definitions in %nan_channels.
+ * @nan_avail_blob: pointer to NAN Availability attribute blob.
+ * See %NL80211_ATTR_NAN_AVAIL_BLOB for more details.
+ * @nan_avail_blob_len: length of the @nan_avail_blob in bytes.
+ * @deferred: if true, the command containing this schedule configuration is a
+ * request from the device to perform an announced schedule update. This
+ * means that it needs to send the updated NAN availability to the peers,
+ * and do the actual switch on the right time (i.e. at the end of the slot
+ * after the slot in which the updated NAN Availability was sent).
+ * See %NL80211_ATTR_NAN_SCHED_DEFERRED for more details.
+ * If false, the schedule is applied immediately.
+ * @nan_channels: array of NAN channel definitions that can be scheduled.
+ */
+struct cfg80211_nan_local_sched {
+ u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS];
+ u8 n_channels;
+ const u8 *nan_avail_blob;
+ u16 nan_avail_blob_len;
+ bool deferred;
+ struct cfg80211_nan_channel nan_channels[] __counted_by(n_channels);
+};
+
+/**
+ * struct cfg80211_nan_peer_map - NAN peer schedule map
+ *
+ * This struct defines a single NAN peer schedule map
+ *
+ * @map_id: map ID of this schedule map
+ * @schedule: a mapping of time slots to chandef indexes in the schedule's
+ * @nan_channels. Each slot lasts 16TUs. An unscheduled slot will be
+ * set to %NL80211_NAN_SCHED_NOT_AVAIL_SLOT.
+ */
+struct cfg80211_nan_peer_map {
+ u8 map_id;
+ u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS];
+};
+
+#define CFG80211_NAN_MAX_PEER_MAPS 2
+#define CFG80211_NAN_INVALID_MAP_ID 0xff
+
+/**
+ * struct cfg80211_nan_peer_sched - NAN peer schedule
+ *
+ * This struct defines NAN peer schedule parameters for a peer.
+ *
+ * @peer_addr: MAC address of the peer (NMI address)
+ * @seq_id: sequence ID of the peer schedule.
+ * @committed_dw: committed DW as published by the peer.
+ * See %NL80211_ATTR_NAN_COMMITTED_DW
+ * @max_chan_switch: maximum channel switch time in microseconds as published
+ * by the peer. See %NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME.
+ * @init_ulw: initial ULWs as published by the peer.
+ * @ulw_size: number of bytes in @init_ulw.
+ * @n_channels: number of channel definitions in @nan_channels.
+ * @nan_channels: array of NAN channel definitions for this schedule.
+ * @maps: array of peer schedule maps. Unused entries have
+ * map_id = %CFG80211_NAN_INVALID_MAP_ID.
+ */
+struct cfg80211_nan_peer_sched {
+ const u8 *peer_addr;
+ u8 seq_id;
+ u16 committed_dw;
+ u16 max_chan_switch;
+ const u8 *init_ulw;
+ u16 ulw_size;
+ u8 n_channels;
+ struct cfg80211_nan_channel *nan_channels;
+ struct cfg80211_nan_peer_map maps[CFG80211_NAN_MAX_PEER_MAPS];
+};
+
/**
* enum cfg80211_nan_conf_changes - indicates changed fields in NAN
* configuration
@@ -4830,6 +5003,19 @@ struct mgmt_frame_regs {
* @nan_change_conf: changes NAN configuration. The changed parameters must
* be specified in @changes (using &enum cfg80211_nan_conf_changes);
* All other parameters must be ignored.
+ * @nan_set_local_sched: configure the local schedule for NAN. The schedule
+ * consists of an array of %cfg80211_nan_channel and the schedule itself,
+ * in which each entry maps each time slot to the channel on which the
+ * radio should operate on. If the chandef of a NAN channel is not
+ * changed, the channel entry must also remain unchanged. It is the
+ * driver's responsibility to verify this.
+ * @nan_set_peer_sched: configure the peer schedule for NAN. The schedule
+ * consists of an array of %cfg80211_nan_channel and the schedule itself,
+ * in which each entry maps each time slot to a channel on which the
+ * radio should operate on. In addition, it contains more peer's schedule
+ * information such as committed DW, etc. When updating an existing peer
+ * schedule, the full new schedule is provided - partial updates are not
+ * supported, and the new schedule completely replaces the previous one.
*
* @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
*
@@ -5207,7 +5393,12 @@ struct cfg80211_ops {
struct wireless_dev *wdev,
struct cfg80211_nan_conf *conf,
u32 changes);
-
+ int (*nan_set_local_sched)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_local_sched *sched);
+ int (*nan_set_peer_sched)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_peer_sched *sched);
int (*set_multicast_to_unicast)(struct wiphy *wiphy,
struct net_device *dev,
const bool enabled);
@@ -5836,6 +6027,12 @@ enum wiphy_nan_flags {
* @max_channel_switch_time: maximum channel switch time in milliseconds.
* @dev_capabilities: NAN device capabilities as defined in Wi-Fi Aware (TM)
* specification Table 79 (Capabilities field).
+ * @phy: Band-agnostic capabilities for NAN data interfaces. Since NAN
+ * operates on multiple channels simultaneously, these capabilities apply
+ * across all bands. Valid only if NL80211_IFTYPE_NAN_DATA is supported.
+ * @phy.ht: HT capabilities (mandatory for NAN data)
+ * @phy.vht: VHT capabilities (optional)
+ * @phy.he: HE capabilities (optional)
*/
struct wiphy_nan_capa {
u32 flags;
@@ -5843,6 +6040,11 @@ struct wiphy_nan_capa {
u8 n_antennas;
u16 max_channel_switch_time;
u8 dev_capabilities;
+ struct {
+ struct ieee80211_sta_ht_cap ht;
+ struct ieee80211_sta_vht_cap vht;
+ struct ieee80211_sta_he_cap he;
+ } phy;
};
#define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
@@ -6736,8 +6938,8 @@ enum ieee80211_ap_reg_power {
* the P2P Device.
* @ps: powersave mode is enabled
* @ps_timeout: dynamic powersave timeout
- * @ap_unexpected_nlportid: (private) netlink port ID of application
- * registered for unexpected class 3 frames (AP mode)
+ * @unexpected_nlportid: (private) netlink port ID of application
+ * registered for unexpected frames (AP mode or NAN_DATA mode)
* @conn: (private) cfg80211 software SME connection state machine data
* @connect_keys: (private) keys to set after connection is established
* @conn_bss_type: connecting/connected BSS type
@@ -6799,7 +7001,7 @@ struct wireless_dev {
bool ps;
int ps_timeout;
- u32 ap_unexpected_nlportid;
+ u32 unexpected_nlportid;
u32 owner_nlportid;
bool nl_owner_dead;
@@ -6859,6 +7061,9 @@ struct wireless_dev {
} ocb;
struct {
u8 cluster_id[ETH_ALEN] __aligned(2);
+ u8 n_channels;
+ struct cfg80211_chan_def *chandefs;
+ bool sched_update_pending;
} nan;
} u;
@@ -9367,9 +9572,10 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
* @addr: the transmitter address
* @gfp: context flags
*
- * This function is used in AP mode (only!) to inform userspace that
- * a spurious class 3 frame was received, to be able to deauth the
- * sender.
+ * This function is used in AP mode to inform userspace that a spurious
+ * class 3 frame was received, to be able to deauth the sender.
+ * It is also used in NAN_DATA mode to report frames from unknown peers
+ * (A2 not assigned to any active NDP), per Wi-Fi Aware (TM) 4.0 specification 6.2.5.
* Return: %true if the frame was passed to userspace (or this failed
* for a reason other than not having a subscription.)
*/
@@ -10016,6 +10222,18 @@ void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
enum nl80211_nan_func_term_reason reason,
u64 cookie, gfp_t gfp);
+/**
+ * cfg80211_nan_sched_update_done - notify deferred schedule update completion
+ * @wdev: the wireless device reporting the event
+ * @success: whether or not the schedule update was successful
+ * @gfp: allocation flags
+ *
+ * This function notifies user space that a deferred local NAN schedule update
+ * (requested with %NL80211_ATTR_NAN_SCHED_DEFERRED) has been completed.
+ */
+void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success,
+ gfp_t gfp);
+
/* ethtool helper */
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
@@ -10356,6 +10574,39 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev,
const u8 *cluster_id, bool new_cluster,
gfp_t gfp);
+/**
+ * cfg80211_nan_ulw_update - Notify user space about ULW update
+ * @wdev: Pointer to the wireless device structure
+ * @ulw: Pointer to the ULW blob data
+ * @ulw_len: Length of the ULW blob in bytes
+ * @gfp: Memory allocation flags
+ *
+ * This function is used by drivers to notify user space when the device's
+ * ULW (Unaligned Schedule) blob has been updated. User space can use this
+ * blob to attach to frames sent to peers.
+ */
+void cfg80211_nan_ulw_update(struct wireless_dev *wdev,
+ const u8 *ulw, size_t ulw_len, gfp_t gfp);
+
+/**
+ * cfg80211_nan_channel_evac - Notify user space about NAN channel evacuation
+ * @wdev: Pointer to the wireless device structure
+ * @chandef: Pointer to the channel definition of the NAN channel that was
+ * evacuated
+ * @gfp: Memory allocation flags
+ *
+ * This function is used by drivers to notify user space when a NAN
+ * channel has been evacuated (i.e. ULWed) due to channel resource conflicts
+ * with other interfaces.
+ * This can happen when another interface sharing the channel resource with NAN
+ * needs to move to a different channel (e.g. due to channel switch or link
+ * switch). User space may reconfigure the local schedule to exclude the
+ * evacuated channel.
+ */
+void cfg80211_nan_channel_evac(struct wireless_dev *wdev,
+ const struct cfg80211_chan_def *chandef,
+ gfp_t gfp);
+
#ifdef CONFIG_CFG80211_DEBUGFS
/**
* wiphy_locked_debugfs_read - do a locked read in debugfs
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 67d764023988..3d55bf4be36f 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -906,8 +906,9 @@
* @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
* (or GO) interface (i.e. hostapd) to ask for unexpected frames to
* implement sending deauth to stations that send unexpected class 3
- * frames. Also used as the event sent by the kernel when such a frame
- * is received.
+ * frames. For NAN_DATA interfaces, this is used to report frames from
+ * unknown peers (A2 not assigned to any active NDP).
+ * Also used as the event sent by the kernel when such a frame is received.
* For the event, the %NL80211_ATTR_MAC attribute carries the TA and
* other attributes like the interface index are present.
* If used as the command it must have an interface index and you can
@@ -1367,6 +1368,53 @@
* %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel
* definition is also sent.
*
+ * @NL80211_CMD_NAN_SET_LOCAL_SCHED: Set the local NAN schedule. NAN must be
+ * operational (%NL80211_CMD_START_NAN was executed). Must contain
+ * %NL80211_ATTR_NAN_TIME_SLOTS and %NL80211_ATTR_NAN_AVAIL_BLOB, but
+ * %NL80211_ATTR_NAN_CHANNEL is optional (for example in case of a channel
+ * removal, that channel won't be provided).
+ * If %NL80211_ATTR_NAN_SCHED_DEFERRED is set, the command is a request
+ * from the device to perform an announced schedule update. See
+ * %NL80211_ATTR_NAN_SCHED_DEFERRED for more details.
+ * If not set, the schedule should be applied immediately.
+ * @NL80211_CMD_NAN_SCHED_UPDATE_DONE: Event sent to user space to notify that
+ * a deferred local NAN schedule update (requested with
+ * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED)
+ * has been completed. The presence of %NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS
+ * indicates that the update was successful.
+ * @NL80211_CMD_NAN_SET_PEER_SCHED: Set the peer NAN schedule. NAN
+ * must be operational (%NL80211_CMD_START_NAN was executed).
+ * Required attributes: %NL80211_ATTR_MAC (peer NMI address) and
+ * %NL80211_ATTR_NAN_COMMITTED_DW.
+ * Optionally, the full schedule can be provided by including all of:
+ * %NL80211_ATTR_NAN_SEQ_ID, %NL80211_ATTR_NAN_CHANNEL (one or more), and
+ * %NL80211_ATTR_NAN_PEER_MAPS (see &enum nl80211_nan_peer_map_attrs).
+ * If any of these three optional attributes is provided, all three must
+ * be provided.
+ * Each peer channel must be compatible with at least one local channel
+ * set by %NL80211_CMD_SET_LOCAL_NAN_SCHED. Different maps must not
+ * contain compatible channels.
+ * For single-radio devices (n_radio <= 1), different maps must not
+ * schedule the same time slot, as the device cannot operate on multiple
+ * channels simultaneously.
+ * When updating an existing peer schedule, the full new schedule must be
+ * provided - partial updates are not supported. The new schedule will
+ * completely replace the previous one.
+ * The peer schedule is automatically removed when the NMI station is
+ * removed.
+ * @NL80211_CMD_NAN_ULW_UPDATE: Notification from the driver to user space
+ * with the updated ULW blob of the device. User space can use this blob
+ * to attach to frames sent to peers. This notification contains
+ * %NL80211_ATTR_NAN_ULW with the ULW blob.
+ * @NL80211_CMD_NAN_CHANNEL_EVAC: Notification to indicate that a NAN
+ * channel has been evacuated due to resource conflicts with other
+ * interfaces. This can happen when another interface sharing the channel
+ * resource with NAN needs to move to a different channel (e.g., channel
+ * switch or link switch on a BSS interface).
+ * The notification contains %NL80211_ATTR_NAN_CHANNEL attribute
+ * identifying the evacuated channel.
+ * User space may reconfigure the local schedule in response to this
+ * notification.
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1632,6 +1680,16 @@ enum nl80211_commands {
NL80211_CMD_INCUMBENT_SIGNAL_DETECT,
+ NL80211_CMD_NAN_SET_LOCAL_SCHED,
+
+ NL80211_CMD_NAN_SCHED_UPDATE_DONE,
+
+ NL80211_CMD_NAN_SET_PEER_SCHED,
+
+ NL80211_CMD_NAN_ULW_UPDATE,
+
+ NL80211_CMD_NAN_CHANNEL_EVAC,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -2659,7 +2717,8 @@ enum nl80211_commands {
* a flow is assigned on each round of the DRR scheduler.
* @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION). Can be set
- * only if %NL80211_STA_FLAG_WME is set.
+ * only if %NL80211_STA_FLAG_WME is set (except for NAN, which uses WME
+ * anyway).
*
* @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include
* in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing
@@ -2991,6 +3050,82 @@ enum nl80211_commands {
* @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable
* this feature during association. This is a flag attribute.
* Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_NAN_CHANNEL: This is a nested attribute. There can be multiple
+ * attributes of this type, each one represents a channel definition and
+ * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ.
+ * When used with %NL80211_CMD_NAN_SET_LOCAL_SCHED, it specifies
+ * the channel definitions on which the radio needs to operate during
+ * specific time slots. All of the channel definitions should be mutually
+ * incompatible. With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and
+ * %NL80211_ATTR_NAN_RX_NSS are mandatory.
+ * When used with %NL80211_CMD_NAN_SET_PEER_SCHED, it configures the
+ * peer NAN channels. In that case, the channel definitions can be
+ * compatible to each other, or even identical just with different RX NSS.
+ * With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and
+ * %NL80211_ATTR_NAN_RX_NSS are mandatory.
+ * The number of channels should fit the current configuration of channels
+ * and the possible interface combinations.
+ * If an existing NAN channel is changed but the chandef isn't, the
+ * channel entry must also remain unchanged.
+ * When used with %NL80211_CMD_NAN_CHANNEL_EVAC, this identifies the
+ * channels that were evacuated.
+ * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the
+ * Channel Entry as defined in Wi-Fi Aware (TM) 4.0 specification Table
+ * 100 (Channel Entry format for the NAN Availability attribute).
+ * @NL80211_ATTR_NAN_RX_NSS: (u8) RX NSS used for a NAN channel. This is
+ * used with %NL80211_ATTR_NAN_CHANNEL when configuring NAN channels with
+ * %NL80211_CMD_NAN_SET_LOCAL_SCHED or %NL80211_CMD_NAN_SET_PEER_SCHED.
+ * @NL80211_ATTR_NAN_TIME_SLOTS: an array of u8 values and 32 cells. each value
+ * maps a time slot to the chandef on which the radio should operate on in
+ * that time. %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled.
+ * The chandef is represented using its index, where the index is the
+ * sequential number of the %NL80211_ATTR_NAN_CHANNEL attribute within all
+ * the attributes of this type.
+ * Each slots spans over 16TUs, hence the entire schedule spans over
+ * 512TUs. Other slot durations and periods are currently not supported.
+ * @NL80211_ATTR_NAN_AVAIL_BLOB: (Binary) The NAN Availability attribute blob,
+ * including the attribute header, as defined in Wi-Fi Aware (TM) 4.0
+ * specification Table 93 (NAN Availability attribute format). Required with
+ * %NL80211_CMD_NAN_SET_LOCAL_SCHED to provide the raw NAN Availability
+ * attribute. Used by the device to publish Schedule Update NAFs.
+ * @NL80211_ATTR_NAN_SCHED_DEFERRED: Flag attribute used with
+ * %NL80211_CMD_NAN_SET_LOCAL_SCHED. When present, the command is a
+ * request from the device to perform an announced schedule update. This
+ * means that it needs to send the updated NAN availability to the peers,
+ * and do the actual switch on the right time (i.e. at the end of the slot
+ * after the slot in which the updated NAN Availability was sent). Since
+ * the slots management is done in the device, the update to the peers
+ * needs to be sent by the device, so it knows the actual switch time.
+ * If the flag is not set, the schedule should be applied immediately.
+ * When this flag is set, the total number of NAN channels from both the
+ * old and new schedules must not exceed the allowed number of local NAN
+ * channels, because with deferred scheduling the old channels cannot be
+ * removed before adding the new ones to free up space.
+ * @NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS: flag attribute used with
+ * %NL80211_CMD_NAN_SCHED_UPDATE_DONE to indicate that the deferred
+ * schedule update completed successfully. If this flag is not present,
+ * the update failed.
+ * @NL80211_ATTR_NAN_NMI_MAC: The address of the NMI station to which this NDI
+ * station belongs. Used with %NL80211_CMD_NEW_STATION when adding an NDI
+ * station.
+ * @NL80211_ATTR_NAN_ULW: (Binary) The initial ULW(s) as published by the
+ * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 109
+ * (Unaligned Schedule attribute format). Used to configure the device
+ * with the initial ULW(s) of a peer, before the device starts tracking it.
+ * @NL80211_ATTR_NAN_COMMITTED_DW: (u16) The committed DW as published by the
+ * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 80
+ * (Committed DW Information field format).
+ * @NL80211_ATTR_NAN_SEQ_ID: (u8) The sequence ID of the peer schedule that
+ * %NL80211_CMD_NAN_SET_PEER_SCHED defines. The device follows the
+ * sequence ID in the frames to identify newer schedules. Once a schedule
+ * with a higher sequence ID is received, the device may stop communicating
+ * with that peer until a new peer schedule with a matching sequence ID is
+ * received.
+ * @NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME: (u16) The maximum channel switch
+ * time, in microseconds.
+ * @NL80211_ATTR_NAN_PEER_MAPS: Nested array of peer schedule maps.
+ * Used with %NL80211_CMD_NAN_SET_PEER_SCHED. Contains up to 2 entries,
+ * each containing nested attributes from &enum nl80211_nan_peer_map_attrs.
*
* @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying
* the signal interference bitmap detected on the operating bandwidth for
@@ -3582,6 +3717,22 @@ enum nl80211_attrs {
NL80211_ATTR_UHR_OPERATION,
+ NL80211_ATTR_NAN_CHANNEL,
+ NL80211_ATTR_NAN_CHANNEL_ENTRY,
+ NL80211_ATTR_NAN_TIME_SLOTS,
+ NL80211_ATTR_NAN_RX_NSS,
+ NL80211_ATTR_NAN_AVAIL_BLOB,
+ NL80211_ATTR_NAN_SCHED_DEFERRED,
+ NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS,
+
+ NL80211_ATTR_NAN_NMI_MAC,
+
+ NL80211_ATTR_NAN_ULW,
+ NL80211_ATTR_NAN_COMMITTED_DW,
+ NL80211_ATTR_NAN_SEQ_ID,
+ NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME,
+ NL80211_ATTR_NAN_PEER_MAPS,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3675,6 +3826,9 @@ enum nl80211_attrs {
* @NL80211_IFTYPE_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_NAN_DATA: NAN data interface type (netdev); NAN data
+ * interfaces can only be brought up (IFF_UP) when a NAN interface
+ * already exists and NAN has been started (using %NL80211_CMD_START_NAN).
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @NUM_NL80211_IFTYPES: number of defined interface types
*
@@ -3696,6 +3850,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_P2P_DEVICE,
NL80211_IFTYPE_OCB,
NL80211_IFTYPE_NAN,
+ NL80211_IFTYPE_NAN_DATA,
/* keep last */
NUM_NL80211_IFTYPES,
@@ -4385,6 +4540,46 @@ enum nl80211_band_attr {
#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
/**
+ * enum nl80211_nan_phy_cap_attr - NAN PHY capabilities attributes
+ * @__NL80211_NAN_PHY_CAP_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET: 16-byte attribute containing HT MCS set
+ * @NL80211_NAN_PHY_CAP_ATTR_HT_CAPA: HT capabilities (u16)
+ * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR: HT A-MPDU factor (u8)
+ * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY: HT A-MPDU density (u8)
+ * @NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET: 8-byte attribute containing VHT MCS set
+ * @NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA: VHT capabilities (u32)
+ * @NL80211_NAN_PHY_CAP_ATTR_HE_MAC: HE MAC capabilities
+ * @NL80211_NAN_PHY_CAP_ATTR_HE_PHY: HE PHY capabilities
+ * @NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET: HE supported NSS/MCS combinations
+ * @NL80211_NAN_PHY_CAP_ATTR_HE_PPE: HE PPE thresholds
+ * @NL80211_NAN_PHY_CAP_ATTR_MAX: highest NAN PHY cap attribute number
+ * @__NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_nan_phy_cap_attr {
+ __NL80211_NAN_PHY_CAP_ATTR_INVALID,
+
+ /* HT capabilities */
+ NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET,
+ NL80211_NAN_PHY_CAP_ATTR_HT_CAPA,
+ NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR,
+ NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY,
+
+ /* VHT capabilities */
+ NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET,
+ NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA,
+
+ /* HE capabilities */
+ NL80211_NAN_PHY_CAP_ATTR_HE_MAC,
+ NL80211_NAN_PHY_CAP_ATTR_HE_PHY,
+ NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET,
+ NL80211_NAN_PHY_CAP_ATTR_HE_PPE,
+
+ /* keep last */
+ __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST,
+ NL80211_NAN_PHY_CAP_ATTR_MAX = __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_wmm_rule - regulatory wmm rule
*
* @__NL80211_WMMR_INVALID: attribute number 0 is reserved
@@ -8557,6 +8752,8 @@ enum nl80211_s1g_short_beacon_attrs {
* @NL80211_NAN_CAPA_CAPABILITIES: u8 attribute containing the
* capabilities of the device as defined in Wi-Fi Aware (TM)
* specification Table 79 (Capabilities field).
+ * @NL80211_NAN_CAPA_PHY: nested attribute containing band-agnostic
+ * capabilities for NAN data path. See &enum nl80211_nan_phy_cap_attr.
* @__NL80211_NAN_CAPABILITIES_LAST: Internal
* @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute.
*/
@@ -8569,9 +8766,38 @@ enum nl80211_nan_capabilities {
NL80211_NAN_CAPA_NUM_ANTENNAS,
NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME,
NL80211_NAN_CAPA_CAPABILITIES,
+ NL80211_NAN_CAPA_PHY,
/* keep last */
__NL80211_NAN_CAPABILITIES_LAST,
NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1,
};
+/**
+ * enum nl80211_nan_peer_map_attrs - NAN peer schedule map attributes
+ *
+ * Nested attributes used within %NL80211_ATTR_NAN_PEER_MAPS to define
+ * individual peer schedule maps.
+ *
+ * @__NL80211_NAN_PEER_MAP_ATTR_INVALID: Invalid
+ * @NL80211_NAN_PEER_MAP_ATTR_MAP_ID: (u8) The map ID for this schedule map.
+ * @NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS: An array of u8 values with 32 cells.
+ * Each value maps a time slot to a channel index within the schedule's
+ * channel list (%NL80211_ATTR_NAN_CHANNEL attributes).
+ * %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled.
+ * @__NL80211_NAN_PEER_MAP_ATTR_LAST: Internal
+ * @NL80211_NAN_PEER_MAP_ATTR_MAX: Highest peer map attribute
+ */
+enum nl80211_nan_peer_map_attrs {
+ __NL80211_NAN_PEER_MAP_ATTR_INVALID,
+
+ NL80211_NAN_PEER_MAP_ATTR_MAP_ID,
+ NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS,
+
+ /* keep last */
+ __NL80211_NAN_PEER_MAP_ATTR_LAST,
+ NL80211_NAN_PEER_MAP_ATTR_MAX = __NL80211_NAN_PEER_MAP_ATTR_LAST - 1,
+};
+
+#define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c25d7c579952..b6163dcc7e92 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -718,6 +718,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev,
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_NAN_DATA:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
@@ -2140,12 +2141,13 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
return -EINVAL;
if (params->ht_capa)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
params->ht_capa, link_sta);
/* VHT can override some HT caps such as the A-MSDU max length */
if (params->vht_capa)
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
params->vht_capa, NULL,
link_sta);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index fab9c1acd8eb..dd99fdc1ea9d 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -166,6 +166,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
for_each_chanctx_user_all(local, ctx, &iter)
num++;
+ /*
+ * This ctx is in the process of getting used,
+ * take it into consideration
+ */
+ if (ctx->will_be_used)
+ num++;
+
return num;
}
@@ -448,73 +455,93 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link)
}
static enum nl80211_chan_width
+ieee80211_get_width_of_link(struct ieee80211_link_data *link)
+{
+ struct ieee80211_local *local = link->sdata->local;
+
+ switch (link->sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ if (!link->sdata->vif.cfg.assoc) {
+ /*
+ * The AP's sta->bandwidth may not yet be set
+ * at this point (pre-association), so simply
+ * take the width from the chandef. We cannot
+ * have TDLS peers yet (only after association).
+ */
+ return link->conf->chanreq.oper.width;
+ }
+ /*
+ * otherwise just use min_def like in AP, depending on what
+ * we currently think the AP STA (and possibly TDLS peers)
+ * require(s)
+ */
+ fallthrough;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ return ieee80211_get_max_required_bw(link);
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
+ NO_VIRTUAL_MONITOR));
+ fallthrough;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
+ return link->conf->chanreq.oper.width;
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_NAN_DATA:
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ /* Take the lowest possible, so it won't change the max width */
+ return NL80211_CHAN_WIDTH_20_NOHT;
+}
+
+static enum nl80211_chan_width
ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
struct ieee80211_link_data *rsvd_for,
bool check_reserved)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_link_data *link;
enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+ struct ieee80211_chanctx_user_iter iter;
+ struct ieee80211_sub_if_data *sdata;
+ enum nl80211_chan_width width;
if (WARN_ON(check_reserved && rsvd_for))
return ctx->conf.def.width;
- for_each_sdata_link(local, link) {
- enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
-
- if (check_reserved) {
- if (link->reserved_chanctx != ctx)
- continue;
- } else if (link != rsvd_for &&
- rcu_access_pointer(link->conf->chanctx_conf) != &ctx->conf)
- continue;
-
- switch (link->sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- if (!link->sdata->vif.cfg.assoc) {
- /*
- * The AP's sta->bandwidth may not yet be set
- * at this point (pre-association), so simply
- * take the width from the chandef. We cannot
- * have TDLS peers yet (only after association).
- */
- width = link->conf->chanreq.oper.width;
- break;
- }
- /*
- * otherwise just use min_def like in AP, depending on what
- * we currently think the AP STA (and possibly TDLS peers)
- * require(s)
- */
- fallthrough;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- width = ieee80211_get_max_required_bw(link);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- case NL80211_IFTYPE_NAN:
- continue;
- case NL80211_IFTYPE_MONITOR:
- WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
- NO_VIRTUAL_MONITOR));
- fallthrough;
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_OCB:
- width = link->conf->chanreq.oper.width;
- break;
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_UNSPECIFIED:
- case NUM_NL80211_IFTYPES:
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- WARN_ON_ONCE(1);
+ /* When this is true we only care about the reserving links */
+ if (check_reserved) {
+ for_each_chanctx_user_reserved(local, ctx, &iter) {
+ width = ieee80211_get_width_of_link(iter.link);
+ max_bw = max(max_bw, width);
}
+ goto check_monitor;
+ }
+ /* Consider all assigned links */
+ for_each_chanctx_user_assigned(local, ctx, &iter) {
+ width = ieee80211_get_width_of_link(iter.link);
max_bw = max(max_bw, width);
}
+ if (!rsvd_for ||
+ rsvd_for->sdata == rcu_access_pointer(local->monitor_sdata))
+ goto check_monitor;
+
+ /* Consider the link for which this chanctx is reserved/going to be assigned */
+ width = ieee80211_get_width_of_link(rsvd_for);
+ max_bw = max(max_bw, width);
+
+check_monitor:
/* use the configured bandwidth in case of monitor interface */
sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
if (sdata &&
@@ -752,10 +779,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
_ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL);
}
-/* Note: if successful, the returned chanctx is reserved for the link */
+/* Note: if successful, the returned chanctx will_be_used flag is set */
static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local,
- struct ieee80211_link_data *link,
const struct ieee80211_chan_req *chanreq,
enum ieee80211_chanctx_mode mode)
{
@@ -767,9 +793,6 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
return NULL;
- if (WARN_ON(link->reserved_chanctx))
- return NULL;
-
list_for_each_entry(ctx, &local->chanctx_list, list) {
const struct ieee80211_chan_req *compat;
@@ -790,12 +813,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
continue;
/*
- * Reserve the chanctx temporarily, as the driver might change
+ * Mark the chanctx as will be used, as the driver might change
* active links during callbacks we make into it below and/or
* later during assignment, which could (otherwise) cause the
* context to actually be removed.
*/
- link->reserved_chanctx = ctx;
+ ctx->will_be_used = true;
ieee80211_change_chanctx(local, ctx, ctx, compat);
@@ -1438,6 +1461,7 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link)
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
case NUM_NL80211_IFTYPES:
WARN_ON(1);
break;
@@ -2011,6 +2035,36 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
ieee80211_vif_use_reserved_switch(local);
}
+static struct ieee80211_chanctx *
+ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_chan_req *chanreq,
+ enum ieee80211_chanctx_mode mode,
+ bool assign_on_failure,
+ bool *reused_ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx *ctx;
+ int radio_idx;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ ctx = ieee80211_find_chanctx(local, chanreq, mode);
+ if (ctx) {
+ *reused_ctx = true;
+ return ctx;
+ }
+
+ *reused_ctx = false;
+
+ if (!ieee80211_find_available_radio(local, chanreq,
+ sdata->wdev.radio_mask,
+ &radio_idx))
+ return ERR_PTR(-EBUSY);
+
+ return ieee80211_new_chanctx(local, chanreq, mode,
+ assign_on_failure, radio_idx);
+}
+
int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
const struct ieee80211_chan_req *chanreq,
enum ieee80211_chanctx_mode mode,
@@ -2020,8 +2074,7 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx *ctx;
u8 radar_detect_width = 0;
- bool reserved = false;
- int radio_idx;
+ bool reused_ctx = false;
int ret;
lockdep_assert_wiphy(local->hw.wiphy);
@@ -2049,17 +2102,8 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
if (!local->in_reconfig)
__ieee80211_link_release_channel(link, false);
- ctx = ieee80211_find_chanctx(local, link, chanreq, mode);
- /* Note: context is now reserved */
- if (ctx)
- reserved = true;
- else if (!ieee80211_find_available_radio(local, chanreq,
- sdata->wdev.radio_mask,
- &radio_idx))
- ctx = ERR_PTR(-EBUSY);
- else
- ctx = ieee80211_new_chanctx(local, chanreq, mode,
- assign_on_failure, radio_idx);
+ ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode,
+ assign_on_failure, &reused_ctx);
if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx);
goto out;
@@ -2069,10 +2113,13 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
ret = ieee80211_assign_link_chanctx(link, ctx, assign_on_failure);
- if (reserved) {
- /* remove reservation */
- WARN_ON(link->reserved_chanctx != ctx);
- link->reserved_chanctx = NULL;
+ /*
+ * In case an existing channel context is being used, we marked it as
+ * will_be_used, now that it is assigned - clear this indication
+ */
+ if (reused_ctx) {
+ WARN_ON(!ctx->will_be_used);
+ ctx->will_be_used = false;
}
if (ret) {
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index f7b05e59374c..93e0342cff4f 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -108,14 +108,13 @@ static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
}
void
-ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- const u8 *he_cap_ie, u8 he_cap_len,
- const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
- struct link_sta_info *link_sta)
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_sta_he_cap *own_he_cap_ptr,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta)
{
struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
- const struct ieee80211_sta_he_cap *own_he_cap_ptr;
struct ieee80211_sta_he_cap own_he_cap;
struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
u8 he_ppe_size;
@@ -125,12 +124,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
memset(he_cap, 0, sizeof(*he_cap));
- if (!he_cap_ie)
- return;
-
- own_he_cap_ptr =
- ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
- if (!own_he_cap_ptr)
+ if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he)
return;
own_he_cap = *own_he_cap_ptr;
@@ -164,7 +158,7 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
- if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
+ if (he_6ghz_capa)
ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80,
@@ -208,6 +202,23 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
}
void
+ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta)
+{
+ const struct ieee80211_sta_he_cap *own_he_cap =
+ ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+
+ _ieee80211_he_cap_ie_to_sta_he_cap(sdata, own_he_cap, he_cap_ie,
+ he_cap_len,
+ (sband->band == NL80211_BAND_6GHZ) ?
+ he_6ghz_capa : NULL,
+ link_sta);
+}
+
+void
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
const struct ieee80211_he_operation *he_op_ie)
{
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 33f1e1b235e9..410e2354f33a 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -136,7 +136,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_ht_cap *own_cap_ptr,
const struct ieee80211_ht_cap *ht_cap_ie,
struct link_sta_info *link_sta)
{
@@ -151,12 +151,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
memset(&ht_cap, 0, sizeof(ht_cap));
- if (!ht_cap_ie || !sband->ht_cap.ht_supported)
+ if (!ht_cap_ie || !own_cap_ptr->ht_supported)
goto apply;
ht_cap.ht_supported = true;
- own_cap = sband->ht_cap;
+ own_cap = *own_cap_ptr;
/*
* If user has specified capability over-rides, take care
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 0298272c37ec..1e1ab25d9d8d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -1014,7 +1014,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
- rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata,
+ &sband->ht_cap,
&htcap_ie,
&sta->deflink);
@@ -1033,6 +1034,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
&chandef);
memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
&cap_ie, NULL,
&sta->deflink);
if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap)))
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d71e0c6d2165..bacb49ad2817 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -928,6 +928,9 @@ struct ieee80211_chanctx {
bool radar_detected;
+ /* This chanctx is in process of getting used */
+ bool will_be_used;
+
/* MUST be last - ends in a flexible-array member. */
struct ieee80211_chanctx_conf conf;
};
@@ -2185,7 +2188,7 @@ void ieee80211_aggr_check(struct ieee80211_sub_if_data *sdata,
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap);
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_ht_cap *own_cap,
const struct ieee80211_ht_cap *ht_cap_ie,
struct link_sta_info *link_sta);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
@@ -2270,6 +2273,7 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
void
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_vht_cap *own_vht_cap,
const struct ieee80211_vht_cap *vht_cap_ie,
const struct ieee80211_vht_cap *vht_cap_ie2,
struct link_sta_info *link_sta);
@@ -2310,6 +2314,12 @@ ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta);
/* HE */
void
+_ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_sta_he_cap *own_he_cap,
+ const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct link_sta_info *link_sta);
+void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 40ce0bb72726..125897717a4c 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1222,14 +1222,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
}
}
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
if (ret) {
kfree(sdata);
return ret;
}
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
mutex_lock(&local->iflist_mtx);
rcu_assign_pointer(local->monitor_sdata, sdata);
mutex_unlock(&local->iflist_mtx);
@@ -1242,6 +1242,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
synchronize_net();
drv_remove_interface(local, sdata);
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
kfree(sdata);
return ret;
}
@@ -1360,8 +1361,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
break;
}
case NL80211_IFTYPE_AP:
- sdata->bss = &sdata->u.ap;
- break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_MONITOR:
@@ -1369,6 +1368,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -1386,8 +1386,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
local->reconfig_failure = false;
res = drv_start(local);
- if (res)
- goto err_del_bss;
+ if (res) {
+ /*
+ * no need to worry about AP_VLAN cleanup since in that
+ * case we can't have open_count == 0
+ */
+ return res;
+ }
ieee80211_led_radio(local, true);
ieee80211_mod_tpt_led_trig(local,
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
@@ -1458,6 +1463,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
netif_carrier_on(dev);
list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list);
break;
+ case NL80211_IFTYPE_AP:
+ sdata->bss = &sdata->u.ap;
+ fallthrough;
default:
if (coming_up) {
ieee80211_del_virtual_monitor(local);
@@ -1546,12 +1554,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
err_stop:
if (!local->open_count)
drv_stop(local, false);
- err_del_bss:
- sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
list_del(&sdata->u.vlan.list);
- /* might already be clear but that doesn't matter */
- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+ /* Might not be initialized yet, but it is harmless */
+ sdata->bss = NULL;
return res;
}
@@ -1940,6 +1946,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_P2P_DEVICE:
sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
+ case NL80211_IFTYPE_NAN_DATA:
+ break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
case NUM_NL80211_IFTYPES:
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7d823a55636f..803106fc3134 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -450,12 +450,13 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
sta->sta.deflink.supp_rates[sband->band] = rates;
- if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
elems->ht_cap_elem,
&sta->deflink))
changed |= IEEE80211_RC_BW_CHANGED;
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
elems->vht_cap_elem, NULL,
&sta->deflink);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0cd8d07bf668..7fc5616cb244 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5586,7 +5586,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
/* Set up internal HT/VHT capabilities */
if (elems->ht_cap_elem && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap,
elems->ht_cap_elem,
link_sta);
@@ -5622,6 +5622,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
}
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ &sband->vht_cap,
elems->vht_cap_elem,
bss_vht_cap, link_sta);
rcu_read_unlock();
@@ -10458,8 +10459,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
pos = mgmt->u.action.ml_reconf_resp.variable;
len -= offsetofend(typeof(*mgmt), u.action.ml_reconf_resp);
- /* each status duple is 3 octets */
- if (len < mgmt->u.action.ml_reconf_resp.count * 3) {
+ if (len < mgmt->u.action.ml_reconf_resp.count *
+ sizeof(struct ieee80211_ml_reconf_status)) {
sdata_info(sdata,
"mlo: reconf: unexpected len=%zu, count=%u\n",
len, mgmt->u.action.ml_reconf_resp.count);
@@ -10468,9 +10469,11 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
link_mask = sta_changed_links;
for (i = 0; i < mgmt->u.action.ml_reconf_resp.count; i++) {
- u16 status = get_unaligned_le16(pos + 1);
+ struct ieee80211_ml_reconf_status *reconf_status = (void *)pos;
+ u16 status = le16_to_cpu(reconf_status->status);
- link_id = *pos;
+ link_id = u8_get_bits(reconf_status->info,
+ IEEE80211_ML_RECONF_LINK_ID_MASK);
if (!(link_mask & BIT(link_id))) {
sdata_info(sdata,
@@ -10505,8 +10508,8 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.reconf.added_links &= ~BIT(link_id);
}
- pos += 3;
- len -= 3;
+ pos += sizeof(*reconf_status);
+ len -= sizeof(*reconf_status);
}
if (link_mask) {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 19c33f7a8193..d9a654ef082d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4607,6 +4607,8 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
(ieee80211_is_public_action(hdr, skb->len) ||
(ieee80211_is_auth(hdr->frame_control) &&
ether_addr_equal(sdata->vif.addr, hdr->addr1)));
+ case NL80211_IFTYPE_NAN_DATA:
+ return false;
default:
break;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 1f0c07eaad1b..e5968d754f8b 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1778,9 +1778,8 @@ TRACE_EVENT(drv_switch_vif_chanctx,
SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type);
SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p);
SWITCH_ENTRY_ASSIGN(link_id, link_conf->link_id);
- strncpy(local_vifs[i].vif.vif_name,
- sdata->name,
- sizeof(local_vifs[i].vif.vif_name));
+ strscpy_pad(local_vifs[i].vif.vif_name,
+ sdata->name);
SWITCH_ENTRY_ASSIGN(old_chandef.control_freq,
old_ctx->def.chan->center_freq);
SWITCH_ENTRY_ASSIGN(old_chandef.freq_offset,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 55054de62508..8987a4504520 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2118,6 +2118,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
return res;
}
break;
+ case NL80211_IFTYPE_NAN_DATA:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 80120f9f17b6..a6570781740a 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -4,7 +4,7 @@
*
* Portions of this file
* Copyright(c) 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2018-2026 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -115,6 +115,7 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
void
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
+ const struct ieee80211_sta_vht_cap *own_vht_cap,
const struct ieee80211_vht_cap *vht_cap_ie,
const struct ieee80211_vht_cap *vht_cap_ie2,
struct link_sta_info *link_sta)
@@ -122,7 +123,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
struct ieee80211_sta_vht_cap own_cap;
u32 cap_info, i;
- bool have_80mhz;
u32 mpdu_len;
memset(vht_cap, 0, sizeof(*vht_cap));
@@ -130,22 +130,25 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
if (!link_sta->pub->ht_cap.ht_supported)
return;
- if (!vht_cap_ie || !sband->vht_cap.vht_supported)
+ if (!vht_cap_ie || !own_vht_cap->vht_supported)
return;
- /* Allow VHT if at least one channel on the sband supports 80 MHz */
- have_80mhz = false;
- for (i = 0; i < sband->n_channels; i++) {
- if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_NO_80MHZ))
- continue;
+ if (sband) {
+ /* Allow VHT if at least one channel on the sband supports 80 MHz */
+ bool have_80mhz = false;
- have_80mhz = true;
- break;
- }
+ for (i = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_80MHZ))
+ continue;
- if (!have_80mhz)
- return;
+ have_80mhz = true;
+ break;
+ }
+
+ if (!have_80mhz)
+ return;
+ }
/*
* A VHT STA must support 40 MHz, but if we verify that here
@@ -156,7 +159,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
vht_cap->vht_supported = true;
- own_cap = sband->vht_cap;
+ own_cap = *own_vht_cap;
/*
* If user has specified capability overrides, take care
* of that if the station we're setting up is the AP that
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index fa0764ede9c5..8b94c0de80ad 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -317,7 +317,7 @@ static bool cfg80211_valid_center_freq(u32 center,
int step;
/* We only do strict verification on 6 GHz */
- if (center < 5955 || center > 7115)
+ if (center < 5955 || center > 7215)
return true;
bw = nl80211_chan_width_to_mhz(width);
@@ -325,7 +325,7 @@ static bool cfg80211_valid_center_freq(u32 center,
return false;
/* Validate that the channels bw is entirely within the 6 GHz band */
- if (center - bw / 2 < 5945 || center + bw / 2 > 7125)
+ if (center - bw / 2 < 5945 || center + bw / 2 > 7225)
return false;
/* With 320 MHz the permitted channels overlap */
@@ -816,6 +816,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN_DATA:
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_UNSPECIFIED:
@@ -939,6 +940,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
case NL80211_IFTYPE_P2P_DEVICE:
/* Can NAN type be considered as beaconing interface? */
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 23afc250bc10..6783e0672dcb 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -5,7 +5,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -254,6 +254,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
+ struct cfg80211_nan_local_sched empty_sched = {};
+
lockdep_assert_held(&rdev->wiphy.mtx);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
@@ -262,6 +264,15 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
if (!wdev_running(wdev))
return;
+ /*
+ * If there is a scheduled update pending, mark it as canceled, so the
+ * empty schedule will be accepted
+ */
+ wdev->u.nan.sched_update_pending = false;
+
+ /* Unschedule all */
+ cfg80211_nan_set_local_schedule(rdev, wdev, &empty_sched);
+
rdev_stop_nan(rdev, wdev);
wdev->is_running = false;
@@ -270,6 +281,47 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
rdev->opencount--;
}
+int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_local_sched *sched)
+{
+ int ret;
+
+ lockdep_assert_held(&rdev->wiphy.mtx);
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN || !wdev_running(wdev))
+ return -EINVAL;
+
+ if (wdev->u.nan.sched_update_pending)
+ return -EBUSY;
+
+ ret = rdev_nan_set_local_sched(rdev, wdev, sched);
+ if (ret)
+ return ret;
+
+ wdev->u.nan.sched_update_pending = sched->deferred;
+
+ kfree(wdev->u.nan.chandefs);
+ wdev->u.nan.chandefs = NULL;
+ wdev->u.nan.n_channels = 0;
+
+ if (!sched->n_channels)
+ return 0;
+
+ wdev->u.nan.chandefs = kcalloc(sched->n_channels,
+ sizeof(*wdev->u.nan.chandefs),
+ GFP_KERNEL);
+ if (!wdev->u.nan.chandefs)
+ return -ENOMEM;
+
+ for (int i = 0; i < sched->n_channels; i++)
+ wdev->u.nan.chandefs[i] = sched->nan_channels[i].chandef;
+
+ wdev->u.nan.n_channels = sched->n_channels;
+
+ return 0;
+}
+
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@@ -277,16 +329,21 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
ASSERT_RTNL();
+ /*
+ * Some netdev interfaces need to be closed before some non-netdev
+ * ones, i.e. NAN_DATA interfaces need to be closed before the NAN
+ * interface
+ */
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (wdev->netdev) {
dev_close(wdev->netdev);
continue;
}
+ }
- /* otherwise, check iftype */
-
- guard(wiphy)(wiphy);
+ guard(wiphy)(wiphy);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_stop_p2p_device(rdev, wdev);
@@ -344,6 +401,8 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
if (wdev->nl_owner_dead) {
+ cfg80211_close_dependents(rdev, wdev);
+
if (wdev->netdev)
dev_close(wdev->netdev);
@@ -354,6 +413,21 @@ void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
}
}
+void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ ASSERT_RTNL();
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return;
+
+ /* Close all NAN DATA interfaces */
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA)
+ dev_close(wdev->netdev);
+ }
+}
+
static void cfg80211_destroy_iface_wk(struct work_struct *work)
{
struct cfg80211_registered_device *rdev;
@@ -761,6 +835,10 @@ int wiphy_register(struct wiphy *wiphy)
!(wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ)))))
return -EINVAL;
+ if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN_DATA)) &&
+ !wiphy->nan_capa.phy.ht.ht_supported))
+ return -EINVAL;
+
if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)))
return -EINVAL;
@@ -1367,9 +1445,8 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
rdev->num_running_monitor_ifaces += num;
}
-void cfg80211_leave(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- int link_id)
+void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
{
struct net_device *dev = wdev->netdev;
struct cfg80211_sched_scan_request *pos, *tmp;
@@ -1420,6 +1497,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_NAN_DATA:
/* nothing to do */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -1430,6 +1508,19 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
}
}
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id)
+{
+ ASSERT_RTNL();
+
+ /* NAN_DATA interfaces must be closed before stopping NAN */
+ cfg80211_close_dependents(rdev, wdev);
+
+ guard(wiphy)(&rdev->wiphy);
+
+ cfg80211_leave_locked(rdev, wdev, link_id);
+}
+
void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
int link_id, gfp_t gfp)
{
@@ -1445,6 +1536,9 @@ void cfg80211_stop_link(struct wiphy *wiphy, struct wireless_dev *wdev,
trace_cfg80211_stop_link(wiphy, wdev, link_id);
+ if (wdev->iftype == NL80211_IFTYPE_NAN)
+ return;
+
ev = kzalloc_obj(*ev, gfp);
if (!ev)
return;
@@ -1595,10 +1689,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
}
break;
case NETDEV_GOING_DOWN:
- scoped_guard(wiphy, &rdev->wiphy) {
- cfg80211_leave(rdev, wdev, -1);
+ cfg80211_leave(rdev, wdev, -1);
+ scoped_guard(wiphy, &rdev->wiphy)
cfg80211_remove_links(wdev);
- }
/* since we just did cfg80211_leave() nothing to do there */
cancel_work_sync(&wdev->disconnect_wk);
cancel_work_sync(&wdev->pmsr_free_wk);
@@ -1679,6 +1772,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
if (rfkill_blocked(rdev->wiphy.rfkill))
return notifier_from_errno(-ERFKILL);
+
+ /* NAN_DATA interfaces require a running NAN interface */
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ struct wireless_dev *iter;
+ bool nan_started = false;
+
+ list_for_each_entry(iter, &rdev->wiphy.wdev_list, list) {
+ if (iter->iftype == NL80211_IFTYPE_NAN &&
+ wdev_running(iter)) {
+ nan_started = true;
+ break;
+ }
+ }
+
+ if (!nan_started)
+ return notifier_from_errno(-ENOLINK);
+ }
break;
default:
return NOTIFY_DONE;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 6cace846d7a3..ae2d56d3ad90 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -318,6 +318,9 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy,
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev);
+
/* free object */
void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
@@ -541,6 +544,9 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num);
+void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int link_id);
+
void cfg80211_leave(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
int link_id);
@@ -551,6 +557,10 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
+int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_local_sched *sched);
+
struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *rdev,
struct cfg80211_internal_bss *tmp,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 5cd86253a62e..bd72317c4964 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -4,7 +4,7 @@
*
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015 Intel Deutschland GmbH
- * Copyright (C) 2019-2020, 2022-2025 Intel Corporation
+ * Copyright (C) 2019-2020, 2022-2026 Intel Corporation
*/
#include <linux/kernel.h>
@@ -782,8 +782,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
rdev_crit_proto_stop(rdev, wdev);
}
- if (nlportid == wdev->ap_unexpected_nlportid)
- wdev->ap_unexpected_nlportid = 0;
+ if (nlportid == wdev->unexpected_nlportid)
+ wdev->unexpected_nlportid = 0;
}
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
@@ -933,12 +933,17 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
* cfg80211 doesn't track the stations
*/
break;
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
+ if (mgmt->u.action.category !=
+ WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION)
+ err = -EOPNOTSUPP;
+ break;
case NL80211_IFTYPE_P2P_DEVICE:
/*
* fall through, P2P device only supports
* public action frames
*/
- case NL80211_IFTYPE_NAN:
default:
err = -EOPNOTSUPP;
break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e15cd26f3a79..f334cdef8958 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -333,6 +333,97 @@ static int validate_nan_cluster_id(const struct nlattr *attr,
return 0;
}
+static int validate_nan_avail_blob(const struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ const u8 *data = nla_data(attr);
+ unsigned int len = nla_len(attr);
+ u16 attr_len;
+
+ /* Need at least: Attr ID (1) + Length (2) */
+ if (len < 3) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "NAN Availability: Too short (need at least 3 bytes, have %u)",
+ len);
+ return -EINVAL;
+ }
+
+ if (data[0] != 0x12) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "NAN Availability: Invalid Attribute ID 0x%02x (expected 0x12)",
+ data[0]);
+ return -EINVAL;
+ }
+
+ attr_len = get_unaligned_le16(&data[1]);
+
+ if (attr_len != len - 3) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "NAN Availability: Length field (%u) doesn't match data length (%u)",
+ attr_len, len - 3);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_nan_ulw(const struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ const u8 *data = nla_data(attr);
+ unsigned int len = nla_len(attr);
+ unsigned int pos = 0;
+
+ while (pos < len) {
+ u16 attr_len;
+
+ /* Need at least: Attr ID (1) + Length (2) */
+ if (pos + 3 > len) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "ULW: Incomplete header (need 3 bytes, have %u)",
+ len - pos);
+ return -EINVAL;
+ }
+
+ if (data[pos] != 0x17) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "ULW: Invalid Attribute ID 0x%02x (expected 0x17)",
+ data[pos]);
+ return -EINVAL;
+ }
+ pos++;
+
+ /* Length is in little-endian format */
+ attr_len = get_unaligned_le16(&data[pos]);
+ pos += 2;
+
+ /*
+ * Check if length is one of the valid values: 16 (no
+ * channel/band entry included), 18 (band entry included),
+ * 21 (channel entry included without Auxiliary channel bitmap),
+ * or 23 (channel entry included with Auxiliary channel bitmap).
+ */
+ if (attr_len != 16 && attr_len != 18 && attr_len != 21 &&
+ attr_len != 23) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "ULW: Invalid length %u (must be 16, 18, 21, or 23)",
+ attr_len);
+ return -EINVAL;
+ }
+
+ if (pos + attr_len > len) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "ULW: Length field (%u) exceeds remaining data (%u)",
+ attr_len, len - pos);
+ return -EINVAL;
+ }
+
+ pos += attr_len;
+ }
+
+ return 0;
+}
+
static int validate_uhr_capa(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
@@ -556,6 +647,13 @@ nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = {
};
static const struct nla_policy
+nl80211_nan_peer_map_policy[NL80211_NAN_PEER_MAP_ATTR_MAX + 1] = {
+ [NL80211_NAN_PEER_MAP_ATTR_MAP_ID] = NLA_POLICY_MAX(NLA_U8, 15),
+ [NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS] =
+ NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS),
+};
+
+static const struct nla_policy
nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = {
[NL80211_NAN_CONF_CLUSTER_ID] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_cluster_id,
@@ -962,6 +1060,22 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
[NL80211_ATTR_UHR_OPERATION] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_operation),
+ [NL80211_ATTR_NAN_CHANNEL] = NLA_POLICY_NESTED(nl80211_policy),
+ [NL80211_ATTR_NAN_CHANNEL_ENTRY] = NLA_POLICY_EXACT_LEN(6),
+ [NL80211_ATTR_NAN_RX_NSS] = { .type = NLA_U8 },
+ [NL80211_ATTR_NAN_TIME_SLOTS] =
+ NLA_POLICY_EXACT_LEN(CFG80211_NAN_SCHED_NUM_TIME_SLOTS),
+ [NL80211_ATTR_NAN_AVAIL_BLOB] =
+ NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_avail_blob),
+ [NL80211_ATTR_NAN_SCHED_DEFERRED] = { .type = NLA_FLAG },
+ [NL80211_ATTR_NAN_NMI_MAC] = NLA_POLICY_ETH_ADDR,
+ [NL80211_ATTR_NAN_ULW] =
+ NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_ulw),
+ [NL80211_ATTR_NAN_COMMITTED_DW] = { .type = NLA_U16 },
+ [NL80211_ATTR_NAN_SEQ_ID] = { .type = NLA_U8 },
+ [NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 },
+ [NL80211_ATTR_NAN_PEER_MAPS] =
+ NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy),
};
/* policy for the key attributes */
@@ -1722,6 +1836,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
return 0;
return -ENOLINK;
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN))
return 0;
@@ -2678,6 +2793,68 @@ fail:
return -ENOBUFS;
}
+static int nl80211_put_nan_phy_cap(struct wiphy *wiphy, struct sk_buff *msg)
+{
+ struct nlattr *nl_phy_cap;
+ const struct ieee80211_sta_ht_cap *ht_cap;
+ const struct ieee80211_sta_vht_cap *vht_cap;
+ const struct ieee80211_sta_he_cap *he_cap;
+
+ if (!cfg80211_iftype_allowed(wiphy, NL80211_IFTYPE_NAN_DATA, false, 0))
+ return 0;
+
+ ht_cap = &wiphy->nan_capa.phy.ht;
+ vht_cap = &wiphy->nan_capa.phy.vht;
+ he_cap = &wiphy->nan_capa.phy.he;
+
+ /* HT is mandatory */
+ if (WARN_ON(!ht_cap->ht_supported))
+ return 0;
+
+ nl_phy_cap = nla_nest_start_noflag(msg, NL80211_NAN_CAPA_PHY);
+ if (!nl_phy_cap)
+ return -ENOBUFS;
+
+ if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET,
+ sizeof(ht_cap->mcs), &ht_cap->mcs) ||
+ nla_put_u16(msg, NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, ht_cap->cap) ||
+ nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR,
+ ht_cap->ampdu_factor) ||
+ nla_put_u8(msg, NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY,
+ ht_cap->ampdu_density))
+ goto fail;
+
+ if (vht_cap->vht_supported) {
+ if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET,
+ sizeof(vht_cap->vht_mcs), &vht_cap->vht_mcs) ||
+ nla_put_u32(msg, NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA,
+ vht_cap->cap))
+ goto fail;
+ }
+
+ if (he_cap->has_he) {
+ if (nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MAC,
+ sizeof(he_cap->he_cap_elem.mac_cap_info),
+ he_cap->he_cap_elem.mac_cap_info) ||
+ nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PHY,
+ sizeof(he_cap->he_cap_elem.phy_cap_info),
+ he_cap->he_cap_elem.phy_cap_info) ||
+ nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET,
+ sizeof(he_cap->he_mcs_nss_supp),
+ &he_cap->he_mcs_nss_supp) ||
+ nla_put(msg, NL80211_NAN_PHY_CAP_ATTR_HE_PPE,
+ sizeof(he_cap->ppe_thres), he_cap->ppe_thres))
+ goto fail;
+ }
+
+ nla_nest_end(msg, nl_phy_cap);
+ return 0;
+
+fail:
+ nla_nest_cancel(msg, nl_phy_cap);
+ return -ENOBUFS;
+}
+
static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg)
{
struct nlattr *nan_caps;
@@ -2704,6 +2881,9 @@ static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg)
wiphy->nan_capa.dev_capabilities))
goto fail;
+ if (nl80211_put_nan_phy_cap(wiphy, msg))
+ goto fail;
+
nla_nest_end(msg, nan_caps);
return 0;
@@ -4879,6 +5059,8 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
else
dev_close(wdev->netdev);
+ cfg80211_close_dependents(rdev, wdev);
+
mutex_lock(&rdev->wiphy.mtx);
return cfg80211_remove_virtual_intf(rdev, wdev);
@@ -7123,6 +7305,26 @@ static int parse_station_flags(struct genl_info *info,
if ((params->sta_flags_mask |
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
return -EINVAL;
+
+ if ((iftype == NL80211_IFTYPE_NAN ||
+ iftype == NL80211_IFTYPE_NAN_DATA) &&
+ params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_MFP)))
+ return -EINVAL;
+
+ /* WME is always used in NAN */
+ if (iftype == NL80211_IFTYPE_NAN_DATA) {
+ /* but don't let userspace control it */
+ if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))
+ return -EINVAL;
+
+ params->sta_flags_mask |= BIT(NL80211_STA_FLAG_WME);
+ params->sta_flags_set |= BIT(NL80211_STA_FLAG_WME);
+ }
+
return 0;
}
@@ -8005,7 +8207,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
/* nl80211_prepare_wdev_dump acquired it in the successful case */
__acquire(&rdev->wiphy.mtx);
- if (!wdev->netdev) {
+ if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) {
err = -EINVAL;
goto out_err;
}
@@ -8192,10 +8394,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
return -EINVAL;
if (params->link_sta_params.supported_rates)
return -EINVAL;
- if (params->ext_capab || params->link_sta_params.ht_capa ||
- params->link_sta_params.vht_capa ||
- params->link_sta_params.he_capa ||
- params->link_sta_params.eht_capa ||
+ if (statype != CFG80211_STA_NAN_MGMT &&
+ (params->link_sta_params.ht_capa ||
+ params->link_sta_params.vht_capa ||
+ params->link_sta_params.he_capa))
+ return -EINVAL;
+ if (params->ext_capab || params->link_sta_params.eht_capa ||
params->link_sta_params.uhr_capa)
return -EINVAL;
if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU))
@@ -8267,6 +8471,19 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
params->plink_action != NL80211_PLINK_ACTION_BLOCK)
return -EINVAL;
break;
+ case CFG80211_STA_NAN_MGMT:
+ if (params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_MFP)))
+ return -EINVAL;
+ break;
+ case CFG80211_STA_NAN_DATA:
+ if (params->sta_flags_mask &
+ ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+ BIT(NL80211_STA_FLAG_MFP) |
+ BIT(NL80211_STA_FLAG_WME)))
+ return -EINVAL;
+ break;
}
/*
@@ -8481,7 +8698,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
- if (!dev)
+ if (!dev && wdev->iftype != NL80211_IFTYPE_NAN &&
+ wdev->iftype != NL80211_IFTYPE_NAN_DATA)
return -EINVAL;
if (!rdev->ops->change_station)
@@ -8624,6 +8842,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
break;
default:
err = -EOPNOTSUPP;
@@ -8652,7 +8872,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
- if (!dev)
+ if (!dev && wdev->iftype != NL80211_IFTYPE_NAN)
return -EINVAL;
if (!rdev->ops->add_station)
@@ -8661,15 +8881,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_MAC])
return -EINVAL;
- if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
- return -EINVAL;
+ if (wdev->iftype == NL80211_IFTYPE_NAN ||
+ wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
+ return -EINVAL;
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ if (!info->attrs[NL80211_ATTR_NAN_NMI_MAC])
+ return -EINVAL;
- if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
- return -EINVAL;
+ /* Only NMI stations receive the HT/VHT/HE capabilities */
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
+ info->attrs[NL80211_ATTR_VHT_CAPABILITY] ||
+ info->attrs[NL80211_ATTR_HE_CAPABILITY])
+ return -EINVAL;
+ }
+ } else {
+ if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+ return -EINVAL;
- if (!info->attrs[NL80211_ATTR_STA_AID] &&
- !info->attrs[NL80211_ATTR_PEER_AID])
- return -EINVAL;
+ if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
+ return -EINVAL;
+
+ if (!info->attrs[NL80211_ATTR_STA_AID] &&
+ !info->attrs[NL80211_ATTR_PEER_AID])
+ return -EINVAL;
+ }
params.link_sta_params.link_id =
nl80211_link_id_or_invalid(info->attrs);
@@ -8685,12 +8921,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
}
- params.link_sta_params.supported_rates =
- nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
- params.link_sta_params.supported_rates_len =
- nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
- params.listen_interval =
- nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
+ if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
+ params.link_sta_params.supported_rates =
+ nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ params.link_sta_params.supported_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
+ }
+
+ if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+ params.listen_interval =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
if (info->attrs[NL80211_ATTR_VLAN_ID])
params.vlan_id = nla_get_u16(info->attrs[NL80211_ATTR_VLAN_ID]);
@@ -8709,7 +8949,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_PEER_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
- else
+ else if (info->attrs[NL80211_ATTR_STA_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
@@ -8830,6 +9070,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ if (wdev->iftype == NL80211_IFTYPE_NAN ||
+ wdev->iftype == NL80211_IFTYPE_NAN_DATA) {
+ if (params.sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
+ return -EINVAL;
+ /* NAN NMI station must be added in associated or authorized state */
+ if (!(params.sta_flags_set & (BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED))))
+ return -EINVAL;
+ }
+
/* Ensure that HT/VHT capabilities are not set for 6 GHz HE STA */
if (params.link_sta_params.he_6ghz_capa &&
(params.link_sta_params.ht_capa || params.link_sta_params.vht_capa))
@@ -8922,6 +9172,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
*/
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
break;
+ case NL80211_IFTYPE_NAN:
+ break;
+ case NL80211_IFTYPE_NAN_DATA:
+ params.nmi_mac = nla_data(info->attrs[NL80211_ATTR_NAN_NMI_MAC]);
+ break;
default:
return -EOPNOTSUPP;
}
@@ -8963,7 +9218,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
- if (!dev)
+ if (!dev && wdev->iftype != NL80211_IFTYPE_NAN)
return -EINVAL;
if (info->attrs[NL80211_ATTR_MAC])
@@ -8974,6 +9229,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
/* always accept these */
break;
case NL80211_IFTYPE_ADHOC:
@@ -13950,6 +14207,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
case NL80211_IFTYPE_P2P_DEVICE:
break;
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
if (!wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN) &&
!(wdev->wiphy->nan_capa.flags &
@@ -14013,6 +14271,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
case NL80211_IFTYPE_P2P_GO:
break;
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_NAN_DATA:
if (!wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_SECURE_NAN) &&
!(wdev->wiphy->nan_capa.flags &
@@ -15520,13 +15779,14 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb,
struct wireless_dev *wdev = dev->ieee80211_ptr;
if (wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype != NL80211_IFTYPE_NAN_DATA)
return -EINVAL;
- if (wdev->ap_unexpected_nlportid)
+ if (wdev->unexpected_nlportid)
return -EBUSY;
- wdev->ap_unexpected_nlportid = info->snd_portid;
+ wdev->unexpected_nlportid = info->snd_portid;
return 0;
}
@@ -15922,6 +16182,10 @@ static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
if (wdev->iftype != NL80211_IFTYPE_NAN)
return -EOPNOTSUPP;
+ cfg80211_close_dependents(rdev, wdev);
+
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_stop_nan(rdev, wdev);
return 0;
@@ -16421,6 +16685,482 @@ nla_put_failure:
}
EXPORT_SYMBOL(cfg80211_nan_func_terminated);
+void cfg80211_nan_sched_update_done(struct wireless_dev *wdev, bool success,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_nan_sched_update_done(wiphy, wdev, success);
+
+ /* Can happen if we stopped NAN */
+ if (!wdev->u.nan.sched_update_pending)
+ return;
+
+ wdev->u.nan.sched_update_pending = false;
+
+ if (!wdev->owner_nlportid)
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_SCHED_UPDATE_DONE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ (success &&
+ nla_put_flag(msg, NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid);
+
+ return;
+
+nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_sched_update_done);
+
+static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev,
+ struct nlattr *channel,
+ struct genl_info *info,
+ struct cfg80211_nan_channel *nan_channels,
+ u8 index, bool local)
+{
+ struct nlattr **channel_parsed __free(kfree) = NULL;
+ struct cfg80211_chan_def chandef;
+ u8 n_rx_nss;
+ int ret;
+
+ channel_parsed = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed),
+ GFP_KERNEL);
+ if (!channel_parsed)
+ return -ENOMEM;
+
+ ret = nla_parse_nested(channel_parsed, NL80211_ATTR_MAX, channel, NULL,
+ info->extack);
+ if (ret)
+ return ret;
+
+ ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed,
+ &chandef);
+ if (ret)
+ return ret;
+
+ if (chandef.chan->band == NL80211_BAND_6GHZ) {
+ NL_SET_ERR_MSG(info->extack,
+ "6 GHz band is not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef,
+ NL80211_IFTYPE_NAN)) {
+ NL_SET_ERR_MSG_ATTR(info->extack, channel,
+ "Channel in NAN schedule is not allowed for NAN operation");
+ return -EINVAL;
+ }
+
+ if (local) {
+ for (int i = 0; i < index; i++) {
+ if (cfg80211_chandef_compatible(&nan_channels[i].chandef,
+ &chandef)) {
+ NL_SET_ERR_MSG_ATTR(info->extack, channel,
+ "Channels in NAN schedule must be mutually incompatible");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (!channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]) {
+ NL_SET_ERR_MSG(info->extack,
+ "Missing NAN channel entry attribute");
+ return -EINVAL;
+ }
+
+ nan_channels[index].channel_entry =
+ nla_data(channel_parsed[NL80211_ATTR_NAN_CHANNEL_ENTRY]);
+
+ if (!channel_parsed[NL80211_ATTR_NAN_RX_NSS]) {
+ NL_SET_ERR_MSG(info->extack,
+ "Missing NAN RX NSS attribute");
+ return -EINVAL;
+ }
+
+ nan_channels[index].rx_nss =
+ nla_get_u8(channel_parsed[NL80211_ATTR_NAN_RX_NSS]);
+
+ n_rx_nss = u8_get_bits(rdev->wiphy.nan_capa.n_antennas, 0x03);
+ if ((local && nan_channels[index].rx_nss > n_rx_nss) ||
+ !nan_channels[index].rx_nss) {
+ NL_SET_ERR_MSG_ATTR(info->extack, channel,
+ "Invalid RX NSS in NAN channel definition");
+ return -EINVAL;
+ }
+
+ nan_channels[index].chandef = chandef;
+
+ return 0;
+}
+
+static int
+nl80211_parse_nan_schedule(struct genl_info *info, struct nlattr *slots_attr,
+ u8 schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS],
+ u8 n_channels)
+{
+ if (WARN_ON(nla_len(slots_attr) != CFG80211_NAN_SCHED_NUM_TIME_SLOTS))
+ return -EINVAL;
+
+ memcpy(schedule, nla_data(slots_attr), nla_len(slots_attr));
+
+ for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) {
+ if (schedule[slot] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT &&
+ schedule[slot] >= n_channels) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Invalid time slot: slot %d refers to channel index %d, n_channels=%d",
+ slot, schedule[slot], n_channels);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+nl80211_parse_nan_peer_map(struct genl_info *info, struct nlattr *map_attr,
+ struct cfg80211_nan_peer_map *map, u8 n_channels)
+{
+ struct nlattr *tb[NL80211_NAN_PEER_MAP_ATTR_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested(tb, NL80211_NAN_PEER_MAP_ATTR_MAX, map_attr,
+ nl80211_nan_peer_map_policy, info->extack);
+ if (ret)
+ return ret;
+
+ if (!tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID] ||
+ !tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS]) {
+ NL_SET_ERR_MSG(info->extack,
+ "Missing required peer map attributes");
+ return -EINVAL;
+ }
+
+ map->map_id = nla_get_u8(tb[NL80211_NAN_PEER_MAP_ATTR_MAP_ID]);
+
+ /* Parse schedule */
+ return nl80211_parse_nan_schedule(info,
+ tb[NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS],
+ map->schedule, n_channels);
+}
+
+static int nl80211_nan_validate_map_pair(struct wiphy *wiphy,
+ struct genl_info *info,
+ const struct cfg80211_nan_peer_map *map1,
+ const struct cfg80211_nan_peer_map *map2,
+ struct cfg80211_nan_channel *nan_channels)
+{
+ /* Check for duplicate map_id */
+ if (map1->map_id == map2->map_id) {
+ NL_SET_ERR_MSG_FMT(info->extack, "Duplicate map_id %u",
+ map1->map_id);
+ return -EINVAL;
+ }
+
+ /* Check for compatible channels between maps */
+ for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) {
+ if (map1->schedule[i] == NL80211_NAN_SCHED_NOT_AVAIL_SLOT)
+ continue;
+
+ for (int j = 0; j < ARRAY_SIZE(map2->schedule); j++) {
+ u8 ch1 = map1->schedule[i];
+ u8 ch2 = map2->schedule[j];
+
+ if (ch2 == NL80211_NAN_SCHED_NOT_AVAIL_SLOT)
+ continue;
+
+ if (cfg80211_chandef_compatible(&nan_channels[ch1].chandef,
+ &nan_channels[ch2].chandef)) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Maps %u and %u have compatible channels %d and %d",
+ map1->map_id, map2->map_id,
+ ch1, ch2);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /*
+ * Check for conflicting time slots between maps.
+ * Only check for single-radio devices (n_radio <= 1) which cannot
+ * operate on multiple channels simultaneously.
+ */
+ if (wiphy->n_radio > 1)
+ return 0;
+
+ for (int i = 0; i < ARRAY_SIZE(map1->schedule); i++) {
+ if (map1->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT &&
+ map2->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Maps %u and %u both schedule slot %d",
+ map1->map_id, map2->map_id, i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int nl80211_nan_set_peer_sched(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_nan_channel *nan_channels __free(kfree) = NULL;
+ struct cfg80211_nan_peer_sched sched = {};
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct nlattr *map_attr, *channel;
+ int ret, n_maps = 0, n_channels = 0, i = 0, rem;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]) {
+ NL_SET_ERR_MSG(info->extack,
+ "Required NAN peer schedule attributes are missing");
+ return -EINVAL;
+ }
+
+ /* First count how many channel attributes we got */
+ nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL,
+ info->nlhdr, GENL_HDRLEN, rem)
+ n_channels++;
+
+ if (!((info->attrs[NL80211_ATTR_NAN_SEQ_ID] &&
+ info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && n_channels) ||
+ ((!info->attrs[NL80211_ATTR_NAN_SEQ_ID] &&
+ !info->attrs[NL80211_ATTR_NAN_PEER_MAPS] && !n_channels)))) {
+ NL_SET_ERR_MSG(info->extack,
+ "Either provide all of: seq id, channels and maps, or none");
+ return -EINVAL;
+ }
+
+ /*
+ * Limit the number of peer channels to:
+ * local_channels * 4 (possible BWs) * 2 (possible NSS values)
+ */
+ if (n_channels && n_channels > wdev->u.nan.n_channels * 4 * 2) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Too many peer channels: %d (max %d)",
+ n_channels,
+ wdev->u.nan.n_channels * 4 * 2);
+ return -EINVAL;
+ }
+
+ if (n_channels) {
+ nan_channels = kcalloc(n_channels, sizeof(*nan_channels),
+ GFP_KERNEL);
+ if (!nan_channels)
+ return -ENOMEM;
+ }
+
+ /* Parse peer channels */
+ nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ bool compatible = false;
+
+ ret = nl80211_parse_nan_channel(rdev, channel, info,
+ nan_channels, i, false);
+ if (ret)
+ return ret;
+
+ /* Verify channel is compatible with at least one local channel */
+ for (int j = 0; j < wdev->u.nan.n_channels; j++) {
+ if (cfg80211_chandef_compatible(&nan_channels[i].chandef,
+ &wdev->u.nan.chandefs[j])) {
+ compatible = true;
+ break;
+ }
+ }
+ if (!compatible) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Channel %d not compatible with any local channel",
+ i);
+ return -EINVAL;
+ }
+ i++;
+ }
+
+ sched.n_channels = n_channels;
+ sched.nan_channels = nan_channels;
+ sched.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ sched.seq_id = nla_get_u8_default(info->attrs[NL80211_ATTR_NAN_SEQ_ID], 0);
+ sched.committed_dw = nla_get_u16(info->attrs[NL80211_ATTR_NAN_COMMITTED_DW]);
+ sched.max_chan_switch =
+ nla_get_u16_default(info->attrs[NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME], 0);
+
+ if (info->attrs[NL80211_ATTR_NAN_ULW]) {
+ sched.ulw_size = nla_len(info->attrs[NL80211_ATTR_NAN_ULW]);
+ sched.init_ulw = nla_data(info->attrs[NL80211_ATTR_NAN_ULW]);
+ }
+
+ /* Initialize all maps as invalid */
+ for (int j = 0; j < ARRAY_SIZE(sched.maps); j++)
+ sched.maps[j].map_id = CFG80211_NAN_INVALID_MAP_ID;
+
+ if (info->attrs[NL80211_ATTR_NAN_PEER_MAPS]) {
+ /* Parse each map */
+ nla_for_each_nested(map_attr, info->attrs[NL80211_ATTR_NAN_PEER_MAPS],
+ rem) {
+ if (n_maps >= ARRAY_SIZE(sched.maps)) {
+ NL_SET_ERR_MSG(info->extack, "Too many peer maps");
+ return -EINVAL;
+ }
+
+ ret = nl80211_parse_nan_peer_map(info, map_attr,
+ &sched.maps[n_maps],
+ n_channels);
+ if (ret)
+ return ret;
+
+ /* Validate against previous maps */
+ for (int j = 0; j < n_maps; j++) {
+ ret = nl80211_nan_validate_map_pair(&rdev->wiphy, info,
+ &sched.maps[j],
+ &sched.maps[n_maps],
+ nan_channels);
+ if (ret)
+ return ret;
+ }
+
+ n_maps++;
+ }
+ }
+
+ /* Verify each channel is scheduled at least once */
+ for (int ch = 0; ch < n_channels; ch++) {
+ bool scheduled = false;
+
+ for (int m = 0; m < n_maps && !scheduled; m++) {
+ for (int s = 0; s < ARRAY_SIZE(sched.maps[m].schedule); s++) {
+ if (sched.maps[m].schedule[s] == ch) {
+ scheduled = true;
+ break;
+ }
+ }
+ }
+ if (!scheduled) {
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "Channel %d is not scheduled in any map",
+ ch);
+ return -EINVAL;
+ }
+ }
+
+ return rdev_nan_set_peer_sched(rdev, wdev, &sched);
+}
+
+static bool nl80211_nan_is_sched_empty(struct cfg80211_nan_local_sched *sched)
+{
+ if (!sched->n_channels)
+ return true;
+
+ for (int i = 0; i < ARRAY_SIZE(sched->schedule); i++) {
+ if (sched->schedule[i] != NL80211_NAN_SCHED_NOT_AVAIL_SLOT)
+ return false;
+ }
+
+ return true;
+}
+
+static int nl80211_nan_set_local_sched(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_nan_local_sched *sched __free(kfree) = NULL;
+ struct wireless_dev *wdev = info->user_ptr[1];
+ int rem, i = 0, n_channels = 0, ret;
+ struct nlattr *channel;
+ bool sched_empty;
+
+ if (wdev->iftype != NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
+ if (!wdev_running(wdev))
+ return -ENOTCONN;
+
+ if (!info->attrs[NL80211_ATTR_NAN_TIME_SLOTS])
+ return -EINVAL;
+
+ /* First count how many channel attributes we got */
+ nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL,
+ info->nlhdr, GENL_HDRLEN, rem)
+ n_channels++;
+
+ sched = kzalloc(struct_size(sched, nan_channels, n_channels),
+ GFP_KERNEL);
+ if (!sched)
+ return -ENOMEM;
+
+ sched->n_channels = n_channels;
+
+ nlmsg_for_each_attr_type(channel, NL80211_ATTR_NAN_CHANNEL,
+ info->nlhdr, GENL_HDRLEN, rem) {
+ ret = nl80211_parse_nan_channel(rdev, channel, info,
+ sched->nan_channels, i, true);
+
+ if (ret)
+ return ret;
+ i++;
+ }
+
+ /* Parse and validate schedule */
+ ret = nl80211_parse_nan_schedule(info,
+ info->attrs[NL80211_ATTR_NAN_TIME_SLOTS],
+ sched->schedule, sched->n_channels);
+ if (ret)
+ return ret;
+
+ sched_empty = nl80211_nan_is_sched_empty(sched);
+
+ sched->deferred =
+ nla_get_flag(info->attrs[NL80211_ATTR_NAN_SCHED_DEFERRED]);
+
+ if (sched_empty) {
+ if (sched->deferred) {
+ NL_SET_ERR_MSG(info->extack,
+ "Schedule cannot be deferred if all time slots are unavailable");
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) {
+ NL_SET_ERR_MSG(info->extack,
+ "NAN Availability blob must be empty if all time slots are unavailable");
+ return -EINVAL;
+ }
+ } else {
+ if (!info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]) {
+ NL_SET_ERR_MSG(info->extack,
+ "NAN Availability blob attribute is required");
+ return -EINVAL;
+ }
+
+ sched->nan_avail_blob =
+ nla_data(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]);
+ sched->nan_avail_blob_len =
+ nla_len(info->attrs[NL80211_ATTR_NAN_AVAIL_BLOB]);
+ }
+
+ return cfg80211_nan_set_local_schedule(rdev, wdev, sched);
+}
+
static int nl80211_get_protocol_features(struct sk_buff *skb,
struct genl_info *info)
{
@@ -18096,7 +18836,11 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info)
NL80211_FLAG_NEED_RTNL) \
SELECTOR(__sel, WIPHY_CLEAR, \
NL80211_FLAG_NEED_WIPHY | \
- NL80211_FLAG_CLEAR_SKB)
+ NL80211_FLAG_CLEAR_SKB) \
+ SELECTOR(__sel, WDEV_UP_RTNL_NOMTX, \
+ NL80211_FLAG_NEED_WDEV_UP | \
+ NL80211_FLAG_NO_WIPHY_MTX | \
+ NL80211_FLAG_NEED_RTNL)
enum nl80211_internal_flags_selector {
#define SELECTOR(_, name, value) NL80211_IFL_SEL_##name,
@@ -18933,6 +19677,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.doit = nl80211_stop_nan,
.flags = GENL_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NO_WIPHY_MTX |
NL80211_FLAG_NEED_RTNL),
},
{
@@ -19227,6 +19972,18 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.flags = GENL_UNS_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
},
+ {
+ .cmd = NL80211_CMD_NAN_SET_LOCAL_SCHED,
+ .doit = nl80211_nan_set_local_sched,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
+ },
+ {
+ .cmd = NL80211_CMD_NAN_SET_PEER_SCHED,
+ .doit = nl80211_nan_set_peer_sched,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
+ },
};
static struct genl_family nl80211_fam __ro_after_init = {
@@ -20527,7 +21284,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct sk_buff *msg;
void *hdr;
- u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid);
+ u32 nlportid = READ_ONCE(wdev->unexpected_nlportid);
if (!nlportid)
return false;
@@ -20567,7 +21324,8 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr,
trace_cfg80211_rx_spurious_frame(dev, addr, link_id);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype != NL80211_IFTYPE_NAN_DATA)) {
trace_cfg80211_return_bool(false);
return false;
}
@@ -22137,6 +22895,97 @@ void cfg80211_nan_cluster_joined(struct wireless_dev *wdev,
}
EXPORT_SYMBOL(cfg80211_nan_cluster_joined);
+void cfg80211_nan_ulw_update(struct wireless_dev *wdev,
+ const u8 *ulw, size_t ulw_len, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_nan_ulw_update(wiphy, wdev, ulw, ulw_len);
+
+ if (!wdev->owner_nlportid)
+ return;
+
+ /* 32 for the wiphy idx, 64 for the wdev id, 100 for padding */
+ msg = nlmsg_new(nla_total_size(sizeof(u32)) +
+ nla_total_size(ulw_len) +
+ nla_total_size(sizeof(u64)) + 100,
+ gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_ULW_UPDATE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ (ulw && ulw_len &&
+ nla_put(msg, NL80211_ATTR_NAN_ULW, ulw_len, ulw)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid);
+
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_ulw_update);
+
+void cfg80211_nan_channel_evac(struct wireless_dev *wdev,
+ const struct cfg80211_chan_def *chandef,
+ gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ struct nlattr *chan_attr;
+ void *hdr;
+
+ trace_cfg80211_nan_channel_evac(wiphy, wdev, chandef);
+
+ if (!wdev->owner_nlportid)
+ return;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_CHANNEL_EVAC);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL);
+ if (!chan_attr)
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, chan_attr);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(wiphy), msg, wdev->owner_nlportid);
+
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_channel_evac);
+
/* initialisation/exit functions */
int __init nl80211_init(void)
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 2bad8b60b7c9..bba239a068f6 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1060,6 +1060,38 @@ rdev_nan_change_conf(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int
+rdev_nan_set_local_sched(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_local_sched *sched)
+{
+ int ret;
+
+ trace_rdev_nan_set_local_sched(&rdev->wiphy, wdev, sched);
+ if (rdev->ops->nan_set_local_sched)
+ ret = rdev->ops->nan_set_local_sched(&rdev->wiphy, wdev, sched);
+ else
+ ret = -EOPNOTSUPP;
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_nan_peer_sched *sched)
+{
+ int ret;
+
+ trace_rdev_nan_set_peer_sched(&rdev->wiphy, wdev, sched);
+ if (rdev->ops->nan_set_peer_sched)
+ ret = rdev->ops->nan_set_peer_sched(&rdev->wiphy, wdev, sched);
+ else
+ ret = -EOPNOTSUPP;
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_acl_data *params)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 20bba7e491c5..5db2121c0b57 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2348,6 +2348,18 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
if (!wdev->netdev || !netif_running(wdev->netdev))
return true;
+ /* NAN doesn't have links, handle it separately */
+ if (iftype == NL80211_IFTYPE_NAN) {
+ for (int i = 0; i < wdev->u.nan.n_channels; i++) {
+ ret = cfg80211_reg_can_beacon(wiphy,
+ &wdev->u.nan.chandefs[i],
+ NL80211_IFTYPE_NAN);
+ if (!ret)
+ return false;
+ }
+ return true;
+ }
+
for (link = 0; link < ARRAY_SIZE(wdev->links); link++) {
struct ieee80211_channel *chan;
@@ -2397,9 +2409,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
continue;
chandef = wdev->u.ocb.chandef;
break;
- case NL80211_IFTYPE_NAN:
- /* we have no info, but NAN is also pretty universal */
- continue;
+ case NL80211_IFTYPE_NAN_DATA:
+ /* NAN channels are checked in NL80211_IFTYPE_NAN interface */
+ break;
default:
/* others not implemented for now */
WARN_ON_ONCE(1);
@@ -2436,11 +2448,14 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy)
struct wireless_dev *wdev;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- guard(wiphy)(wiphy);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ bool valid;
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
- if (!reg_wdev_chan_valid(wiphy, wdev))
+ scoped_guard(wiphy, wiphy)
+ valid = reg_wdev_chan_valid(wiphy, wdev);
+ if (!valid)
cfg80211_leave(rdev, wdev, -1);
+ }
}
static void reg_check_chans_work(struct work_struct *work)
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 3385a27468f7..d45ddc457c30 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -102,25 +102,26 @@ static int wiphy_suspend(struct device *dev)
if (!rdev->wiphy.registered)
goto out_unlock_rtnl;
- wiphy_lock(&rdev->wiphy);
if (rdev->wiphy.wowlan_config) {
- cfg80211_process_wiphy_works(rdev, NULL);
- if (rdev->ops->suspend)
- ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
- if (ret <= 0)
- goto out_unlock_wiphy;
+ scoped_guard(wiphy, &rdev->wiphy) {
+ cfg80211_process_wiphy_works(rdev, NULL);
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev,
+ rdev->wiphy.wowlan_config);
+ if (ret <= 0)
+ goto out_unlock_rtnl;
+ }
}
/* Driver refused to configure wowlan (ret = 1) or no wowlan */
cfg80211_leave_all(rdev);
- cfg80211_process_rdev_events(rdev);
- cfg80211_process_wiphy_works(rdev, NULL);
- if (rdev->ops->suspend)
- ret = rdev_suspend(rdev, NULL);
-
-out_unlock_wiphy:
- wiphy_unlock(&rdev->wiphy);
+ scoped_guard(wiphy, &rdev->wiphy) {
+ cfg80211_process_rdev_events(rdev);
+ cfg80211_process_wiphy_works(rdev, NULL);
+ if (rdev->ops->suspend)
+ ret = rdev_suspend(rdev, NULL);
+ }
out_unlock_rtnl:
if (ret == 0)
rdev->suspended = true;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index af23f4fca90a..eb5bedf9c92a 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -2410,6 +2410,55 @@ TRACE_EVENT(rdev_del_nan_func,
WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
);
+TRACE_EVENT(rdev_nan_set_local_sched,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_local_sched *sched),
+ TP_ARGS(wiphy, wdev, sched),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __array(u8, schedule, CFG80211_NAN_SCHED_NUM_TIME_SLOTS)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ memcpy(__entry->schedule, sched->schedule,
+ CFG80211_NAN_SCHED_NUM_TIME_SLOTS);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", schedule: %s",
+ WIPHY_PR_ARG, WDEV_PR_ARG,
+ __print_array(__entry->schedule,
+ CFG80211_NAN_SCHED_NUM_TIME_SLOTS, 1))
+);
+
+TRACE_EVENT(rdev_nan_set_peer_sched,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_nan_peer_sched *sched),
+ TP_ARGS(wiphy, wdev, sched),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __array(u8, peer_addr, ETH_ALEN)
+ __field(u8, seq_id)
+ __field(u16, committed_dw)
+ __field(u16, max_chan_switch)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ memcpy(__entry->peer_addr, sched->peer_addr, ETH_ALEN);
+ __entry->seq_id = sched->seq_id;
+ __entry->committed_dw = sched->committed_dw;
+ __entry->max_chan_switch = sched->max_chan_switch;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+ ", peer: %pM, seq_id: %u, committed_dw: 0x%x, max_chan_switch: %u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->peer_addr,
+ __entry->seq_id, __entry->committed_dw,
+ __entry->max_chan_switch
+ )
+);
+
TRACE_EVENT(rdev_set_mac_acl,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_acl_data *params),
@@ -4276,6 +4325,62 @@ TRACE_EVENT(cfg80211_incumbent_signal_notify,
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", signal_interference_bitmap=0x%x",
WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->signal_interference_bitmap)
);
+
+TRACE_EVENT(cfg80211_nan_sched_update_done,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, bool success),
+ TP_ARGS(wiphy, wdev, success),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(bool, success)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->success = success;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " success=%d",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->success)
+);
+
+TRACE_EVENT(cfg80211_nan_ulw_update,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *ulw, size_t ulw_len),
+ TP_ARGS(wiphy, wdev, ulw, ulw_len),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __dynamic_array(u8, ulw, ulw_len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ if (ulw && ulw_len)
+ memcpy(__get_dynamic_array(ulw), ulw, ulw_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT " ulw: %s",
+ WIPHY_PR_ARG, WDEV_PR_ARG,
+ __print_array(__get_dynamic_array(ulw),
+ __get_dynamic_array_len(ulw), 1))
+);
+
+TRACE_EVENT(cfg80211_nan_channel_evac,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, wdev, chandef),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WDEV_PR_FMT ", " WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WDEV_PR_ARG, WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 0a0cea018fc5..cff5a1bd95cc 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -90,7 +90,7 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
/* see 802.11ax D6.1 27.3.23.2 */
if (chan == 2)
return MHZ_TO_KHZ(5935);
- if (chan <= 233)
+ if (chan <= 253)
return MHZ_TO_KHZ(5950 + chan * 5);
break;
case NL80211_BAND_60GHZ:
@@ -625,8 +625,9 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC &&
iftype != NL80211_IFTYPE_STATION &&
- iftype != NL80211_IFTYPE_OCB)
- return -1;
+ iftype != NL80211_IFTYPE_OCB &&
+ iftype != NL80211_IFTYPE_NAN_DATA)
+ return -1;
break;
}
@@ -1144,8 +1145,15 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
ev->ij.channel);
break;
case EVENT_STOPPED:
- cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev,
- ev->link_id);
+ /*
+ * for NAN interfaces cfg80211_leave must be called but
+ * locking here doesn't allow this.
+ */
+ if (WARN_ON(wdev->iftype == NL80211_IFTYPE_NAN))
+ break;
+
+ cfg80211_leave_locked(wiphy_to_rdev(wdev->wiphy), wdev,
+ ev->link_id);
break;
case EVENT_PORT_AUTHORIZED:
__cfg80211_port_authorized(wdev, ev->pa.peer_addr,
@@ -1184,6 +1192,13 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
if (otype == NL80211_IFTYPE_AP_VLAN)
return -EOPNOTSUPP;
+ /*
+ * for NAN interfaces cfg80211_leave must be called for leaving,
+ * but locking here doesn't allow this.
+ */
+ if (otype == NL80211_IFTYPE_NAN)
+ return -EOPNOTSUPP;
+
/* cannot change into P2P device or NAN */
if (ntype == NL80211_IFTYPE_P2P_DEVICE ||
ntype == NL80211_IFTYPE_NAN)
@@ -1204,7 +1219,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
dev->ieee80211_ptr->use_4addr = false;
rdev_set_qos_map(rdev, dev, NULL);
- cfg80211_leave(rdev, dev->ieee80211_ptr, -1);
+ cfg80211_leave_locked(rdev, dev->ieee80211_ptr, -1);
cfg80211_process_rdev_events(rdev);
cfg80211_mlme_purge_registrations(dev->ieee80211_ptr);
@@ -1232,6 +1247,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_NAN_DATA:
dev->priv_flags |= IFF_DONT_BRIDGE;
break;
case NL80211_IFTYPE_P2P_GO: